Richard Warburton

Unit Testing Lambda Expressions & Streams

Richard Warburton

This is a guest post from Richard Warburton, who has worked as a developer across varied areas including Statistical Analytics, Static Analysis, Compilers and Networking. He is a leader in the London Java Community and runs OpenJDK Hackdays.

Richard also teaches a course on modern development with Java 8 at Skills Matter, alongside Jim Gough & Raoul-Gabriel Urma. At the end of this course, you will be ready to use Java 8 on your day job and be familiar with the cutting edge programming approaches which allow you to write more flexible and concise code. You can find out more information about the course here, or head to the Skills Matter page to book your place now!


Usually, when writing a unit test you call a method in your test code that gets called in your application. Given some inputs and possibly test doubles, you call these methods to test a certain behavior happening and then specify the changes you expect to result from this behavior.

Lambda expressions pose a slightly different challenge when unit testing code. Because they don’t have a name, it’s impossible to directly call them in your test code. You could choose to copy the body of the lambda expression into your test and then test that copy, but this approach has the unfortunate side effect of not actually testing the behavior of your implementation. If you change the implementation code, your test will still pass even though the implementation is performing a different task.

There are two viable solutions to this problem. The first is to view the lambda expression as a block of code within its surrounding method. If you take this approach, you should be testing the behavior of the surrounding method, not the lambda expression itself.

Here’s an example method for converting a list of strings into their uppercase equivalents.

public static List allToUpperCase(List words) {
    return words.stream()
                .map(string -> string.toUpperCase())
                .collect(Collectors.toList());
}

The only thing that the lambda expression in this body of code does is directly call a core Java method. It’s really not worth the effort of testing this lambda expression as an independent unit of code at all, since the behavior is so simple.

If I were to unit test this code, I would focus on the behavior of the method. For example, here is a test that if there are multiple words in the stream, they are all converted to their uppercase equivalents.

@Test
public void multipleWordsToUppercase() {
    List input = Arrays.asList("a", "b", "hello");
    List result = allToUpperCase(input);
    assertEquals(asList("A", "B", "HELLO"), result);
}

Sometimes you want to use a lambda expression that exhibits complex functionality. Perhaps it has a number of corner cases or a role involving calculating a highly important function in your domain. You really want to test for behavior specific to that body of code, but it’s in a lambda expression and you’ve got no way of referencing it.

As an example problem, let’s look at a method that is slightly more complex than converting a list of strings to uppercase. Instead, we’ll be converting the first character of a string to uppercase and leaving the rest as is. If we were to write this using streams and lambda expressions, we might write something like the following.

public static List uppercaseFirstChar(List words) {
    return words.stream()
                .map(value -> {
                    char firstChar = value.charAt(0);
    firstChar = toUpperCase(firstChar);
                    return firstChar + value.substring(1);
                })
                .collect(Collectors.toList());
}

Should we want to test this, we’d need to fire in a list and test the output for every single
example we wanted to test. The test below provides an example of how cumbersome this
approach becomes. Don’t worry—there is a solution!

@Test
public void twoLetterStringConvertedToUppercaseLambdas() {
    List input = Arrays.asList("ab");
    List result = uppercaseFirstChar(input);
    assertEquals(Arrays.asList("Ab"), result);
}

Don’t use a lambda expression! I know that might appear to be strange advice in an article about lambda expressions, but square pegs don’t fit into round holes very well. Having accepted this, we’re bound to ask how we can still unit test our code and have the benefit of lambda-enabled libraries.

Do use method references. Any method that would have been written as a lambda expression can also be written as a normal method and then directly referenced elsewhere in code using method references. In the code below I’ve refactored out the lambda expression into its own method. This is then used by the main method, which deals with converting the list of strings.

public static List uppercaseFirstChar(List words) {
    return words.stream()
                .map(Testing::firstToUppercase)
                .collect(Collectors.toList());
}

public static String firstToUppercase(String value) {
    char firstChar = value.charAt(0);
    firstChar = toUpperCase(firstChar);
    return firstChar + value.substring(1);
}

Having extracted the method that actually performs string processing, we can cover all the corner cases by testing that method on its own. The same test case in its new, simplified form is shown here:

@Test
public void twoLetterStringConvertedToUppercase() {
    String input = "ab";
    String result = firstToUppercase(input);
    assertEquals("Ab", result);
}

The key takeaway here is that if you want to unit test a lambda expression of serious complexity, extract it to a regular method first. You can then use method references to treat it like a first-class function.


This Week at Skills Matter: 4th November – 8th November

Here’s what’s coming up at Skills Matter this week!

Monday:

Infracoders London will be joining us at Skills Matter HQ for their November meet-up with a talk on ‘A Journey of Windows Infrastructure Automation At thetrainline.com’ – a brief history, where it started, what’s happening now and where it’s heading using Chef, VMWare, power-shell plus a few other tools.

Also on Monday Richard Warburton and Martijn Verburg will be giving a talk on ‘Bleeding Edge’ at the London Java Community November meet-up. Talking about what happens to your technology stack if you’re willing to take a risk?

Tuesday:

On Tuesday Skills Matter will be hosting the London Clojurians November meet-up where they will have three lightening talks from Henry Garner, Kris Jenkins and Jamie Brandon.

blog
We will also host Oren Eini, aka Ayende Rahien, for an In The Brain talk about ‘The DB Disassembly Kit’, going into the details that actually make up the different components in a database, how they are put together and much more!
Finally we will host the London .Net User Group Meet-up where they will have a talk from Gael Fraiteur on ‘Multithreading Design Patterns’. Some things Gael will talk about include: characteristics of a threading model, enforcement and automation of threading model and why do we need threading models?

Wednesday:

 The London Pyramid Group will have their November meet-up with talks from Jon Staley on ‘King Forms’ – a quick intro to Deforem & Colander and how he wrangled them to do what he wanted, and Riley Doyle on ‘From the Script to Web’ – covering key lessons learned in building an advanced ‘DNA search engine’ with Pyramid.

susannemadsen
We will also have another In The Brain session with Susanne Madsen who will be talking about ‘Project Leadership: Are we too busy with the urgent to focus on the important?’. Susanne has years of experience running high-profile IT projects and is a qualified corporate and executive coach – this talk is not to be missed!

Thursday:

London Lua will have a talk on ‘Lua & Corona SDK – Cross platform mobile game development’ for their November meet-up.

The final In the Brain of the week will be with Ian Plosker who will be talking about ‘The Triumph of Simplicity: how database complexity will be replaced by simple services’. Ian will be discussing why the level of complexity in storing and querying data has exploded and discuss how database software eventually will be overrun by simple services.



Stay up to date with the latest from Skills Matter! Follow us on Facebook, Twitter, Google+, or sign up to our newsletter.