Not everything is easy in unit testing. Some code will be downright tricky to test. We’ll work through a couple of examples of how to test some of the more challenging situations. Specifically, we’ll write tests for code that involves threading and persistence.

Testing themes

Our approach to testing threads and persistence will be based on these two themes:

  • Rework the design to better support testing.
  • Then break dependencies using stubs and mocks.

It’s hard enough to write code that works as expected. That’s one reason to write unit tests. It’s dramatically harder to write concurrent code that works.

In one sense, testing application code that requires concurrent processing is technically out of the realm of unit testing. It’s better classified as integration testing since you’re verifying that we can integrate the notion of our application-specific logic with the ability to execute portions of it concurrently.

Slow test

Tests for threaded code tend to be slower since we must expand the scope of execution time to ensure that we have no concurrency issues. Threading defects sometimes sneakily lie in wait, surfacing long after we thought they had all been stomped out.

Keep it simple, smarty!

It is best to follow a couple of primary themes when testing threaded code:

Minimize the overlap

Minimize the overlap between threading controls and application code. We should rework our design so that we can unit-test the bulk of application code in the absence of threads. Write thread-focused tests for the small remainder of the code.

Concurrency utility classes

Trust the work of others. Java 5 incorporated Doug Lea’s wonderful set of concurrency utility classes (found in the java.util.concurrent package), which had already undergone years of hardening by the time Java 5 came out in 2004. Instead of coding producer/consumer yourself by hand (for example), take advantage of the fact that other smart people already went through all the pain and use the proven class BlockingQueue.

Java provides many, many alternatives for supporting concurrency. We’ll touch on just one here and it won’t cover more specific cases much of the time. Remember the two themes, though. This example shows how to redesign code to separate the concerns of threading and application logic.

Matchmaker, matchmaker, find me all matches

Let’s take a look at the ProfileMatcher class, a core piece of iloveyouboss. A ProfileMatcher collects all of the relevant profiles. Given a set of criteria from a client, the ProfileMatcher instance iterates the profiles and returns those matching the criteria, along with the MatchSet instance which allows us to obtain the score of the match:

Get hands-on with 1200+ tech skills courses.