DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Alternative Structured Concurrency
  • Beyond Java Streams: Exploring Alternative Functional Programming Approaches in Java
  • Using Java Stream Gatherers To Improve Stateful Operations
  • Thread-Safety Pitfalls in XML Processing

Trending

  • When One MVP Is Really Four Systems: A Better Way to Plan Multi-Role Apps
  • Mocking Kafka for Local Spring Development
  • Contract-First Integration: Building Scalable Systems With Flyway, OpenAPI, and Kafka
  • Securing the AI Host: Spring AI MCP Server Communication With API Keys
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. DevOps and CI/CD
  4. Unit Testing: Java Streams and Lambdas

Unit Testing: Java Streams and Lambdas

While Java streams and lambdas can seem complex for unit testing, this tutorial demonstrates simplified instructions to perform unit testing in JDK8.

By 
Yogen Rai user avatar
Yogen Rai
·
Manoj Debnath user avatar
Manoj Debnath
·
Updated Jan. 26, 24 · Tutorial
Likes (21)
Comment
Save
Tweet
Share
99.9K Views

Join the DZone community and get the full member experience.

Join For Free

Unit testing is an indispensable practice for Java developers (any developer really). The primary benefit of unit testing a pipeline is the early detection of bugs, which is less expensive to fix. It not only improves the overall quality of the code but also makes future maintenance easy.

Using lambdas specifically with streams in Java makes code concise and readable. Streams are excellent for filtering, mapping, sorting, and reducing operations. The elements in the sequence are better processed in a declarative and functional manner, something which lambdas exactly fit into. The anonymous functions that are written with lambdas not only facilitate a functional style of programming but also enhance the expressiveness of the code. Streams and lambdas were introduced in Java after JDK 8, and since then, Java developers have used these features frequently in their projects.

The question around these components that the article tries to address is, "How can you unit test Java streams and lambdas?"

Importance of Unit Testing Pipelines and Lambdas

In unit testing, an individual piece of component of a software application is tested separately. This small unit of code typically is a function, method, or subroutine. The testing mechanism is automated so that they can be done repeatedly and quickly. The test cases are usually written by developers and integrated into the CI/CD pipeline in the development process. The code can be isolated and problems can be easily identified if we use lambda because the essence of it is to make the program functional, more modular, and reusable — something which makes it friendly for unit testing pipelines.

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 stream above 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 the 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.

As 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 of software development.  With new features introduced after JDK 8, it has become easier to write code concisely and declaratively. However, 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! Learn more about the best Java unit testing frameworks.

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

unit test Stream (computing) Java (programming language) Continuous Integration/Deployment

Opinions expressed by DZone contributors are their own.

Related

  • Alternative Structured Concurrency
  • Beyond Java Streams: Exploring Alternative Functional Programming Approaches in Java
  • Using Java Stream Gatherers To Improve Stateful Operations
  • Thread-Safety Pitfalls in XML Processing

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook