Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Unit Testing: Java Streams and Lambdas

DZone's Guide to

Unit Testing: Java Streams and Lambdas

How can we implement unit testing for the new stream and lambda features in Java? Read this post to learn more!

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

After stream and lambda were introduced in Java after JDK 8, I am sure that most Java developers are actively using these new features in their projects. The questions around these components are how to unit test them? This post talks how to write the unit tests around these features. 

Unit Testing Stream Pipelines

Since stream pipelines combine with lambdas to form a single unit, it is not obvious how to effectively unit test the pieces of the pipeline. 

I have always followed these two guidelines to unit test those stream pipelines:

  1. If the pipeline is simple enough, it can be wrapped in a method call, and it is enough to unit test the method call.

  2. If the pipeline is more complex, pieces of the pipeline can be called from support methods, and the support methods can be unit tested.

For example, let's say we have a stream pipeline that maps all the letters of the word to uppercase and is wrapped in a java.util.function.Function<T,R>  as below:

Function<List<String>, List<String>> allToUpperCase = words -> words.stream().map(String::toUpperCase).collect(Collectors.toList());


Now, the unit test for this stream pipeline can be easily written as ordinary unit testing of the allToUpperCase.

@Test
public void testAllToUpperCase() {    
  List<String> expected = Arrays.asList("JAVA8", "STREAMS");    
  List<String> result = allToUpperCase.apply(Arrays.asList("java8", "streams"));    
  assertEquals(expected, result);
}


The above stream can be wrapped in a regular function, as seen below. Also, an ordinary unit test can be written against this function:

public List<String> convertAllToUpperCase(List<String> words) {    
  return words.stream().map(String::toUpperCase).collect(Collectors.toList());
}


Unit Testing Lambdas

Believe me — it is very likely that you will encounter complex unit testing in real-world programming. The unit testing with complex lambdas, similar to unit testing of stream pipelines, can be simplified with following practices:

  1. Replace a lambda that needs to be tested with a method reference and an auxiliary method.

  2. Then, test the auxiliary method.

For example, I have a stream pipeline that involves a somewhat-complex lambda and a mapping class for the given string class name.

public static Class[] mapClasses(final List<String> exceptions) {    
  return exceptions.stream().map(className -> {        
    try {            
      return Class.forName(className);        
    } catch(Exception ex) {            
      LOGGER.error("Failed to load class for exceptionWhiteList: {}", className);        
    }        
    return null;    
  }).toArray(Class[]::new);
}


Here, the key point to test is whether the expression for transforming a string class name to a Class object is working.

Like mentioned above, this can replace the lambda expression with a method reference, along with an auxiliary method that can be placed in a companion class, as shown below:

public static Class[] mapClassesBetter(final List<String> exceptions) {    
  return exceptions.stream().map(LambdaTester::mapClass).toArray(Class[]::new);
}

public static Class mapClass(String className) {    
  try {        
    return Class.forName(className);    
  } catch(Exception ex) {        
    LOGGER.error("Failed to load class for name: {}", className);    
  }    
  return null;
}


Now, the key element of the original lambda is that it can be tested directly:

@Test
public void testMapClass() throws ClassNotFoundException {    
  assertEquals(null, mapClass("a"));    
  assertEquals(null, mapClass("apple"));    
  assertEquals(Object.class, mapClass("java.lang.Object"));
}


Conclusion

Writing unit tests is one of the core parts in software development.  With new features introduced after JDK 8, it has become easier to write code concisely and declaratively. Only, the proper use of features, like streams and lambda, brings value and, of course, makes writing unit tests easier. If you have any additional guidelines for unit testing these features, don't stop yourself from sharing them in the comments. Until next time, happy coding!

The source code for the examples presented above is available on GitHub.

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. Our APIs verify, standardize, and correct the Big 4 + more – name, email, phone and global addresses – to ensure accurate delivery, prevent blacklisting and identify risks in real-time.

Topics:
unit testing ,streams ,lambda expressions ,java ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}