, ,

In this series on unit testing with JUnit, we started with JUnit tests both using Maven and IntelliJ in the first part. In the second part, we learned about assertions, JUnit 4 annotations, and test suites. In this post, we will cover assertThat, a more expressive style of assertion that uses Hamcrest matchers.

Assertions with assertThat

The classic JUnit assertions, such as assertEquals, assertTrue, and so on are simple to understand and use. But, by using assertThat with Hamcrest matchers, It’s easy to make dramatic improvements to your tests. Hamcrest is a framework that provides support for Java unit testing. Hamcrest contains self-contained classes, called matchers with static methods designed to be used with JUnit assertThat.

What you can do with the classic assertions, you can also do with assertThat, but more fluently, and make tests more readable. For example, take a look at the following assertions:

As it is relevant, the second assertion is more readable. If you read out, the second assertion reads more like a sentence – “Assert that actual is not equal to expected”.

Beside test readability, readability of test failures is another highlight of assertThat, as shown in the following figure.

JUnit Assertion Failure Messages for assertFalse and assertThat

As you can see, the second assertion failure message of assertThat is far more explanatory as compared to assertFalse. It was because we used a core Hamcrest matcher.

Core Matchers

When you write an assertThat method, you pass two parameters to it. The first is the actual result, typically the value/object returned by the method under test. The second parameter is a matcher obtained from a call to a matcher method. The matcher is an object that matches the test rule. To see how it works, we will write a class with few methods that we will test.

MatchersDemo.java

In the class above, we wrote a toConcatedUpperCase() method that concatenates two strings passed as parameters, converts the result to uppercase, and returns it. We then wrote a floatingPointMultiplication() method that returns the product of two double values passed to it. We also wrote the addStringToCollection() and getStringCollection() that adds a string to a Set collection and returns the Set respectively.

We will next write a test class with few test methods.

MatchersDemoTest.java

In the test class above, we started with a static import of the Hamcrest core matcher, in Line 6 . We then used the @Before annotation to instantiate the MatchersDemo class. Recall from the previous post that the @Before annotated method will run before each @Test method in the class. Then, we wrote the assertions:

  • Line 18: We wrote an assertThat with the is method containing the equalTo method. We can read it as-“assert that actual (the value that toConcatedUpperCase() method returns) is equal to expected (HELLOWORLD). Although not necessary, many programmers like to use is and other matcher methods together because it makes assertions more readable. This is the very reason for the existence of is and being referred as decorator: to decorate other matchers.
  • Line 24: We used startsWith to assert that the actual string starts with the expected value, HELLO.
  • Line 31: We used containsString to test that the actual string contains the expected value WORLD.
  • Line 37: We wrote an assertThat with allOf, and things get interesting here. allOf takes multiple matcher methods and returns a matcher. This matcher test if the actual result matches all the specified matchers- think about the Java short circuit && operator. So, in a single assertion, we asserted that the actual result is not a null value, is an instance of the String class, and starts with and contains HELLO. The corresponding matcher method, that works like the Java short circuit || operator is anyOf.

When we run the test class above, all the test passes. But, the core matchers we used are only a subset of the wide range of Hamcrest matchers. There are additional matchers for specific test requirements, such as testing collections, numbers, text comparison, and so on. The aditional matchers are not part of JUnit, and to use them, we need to seperately download the Hamcrest matcher library and point the project classpath to it. If you are using Maven, add the following dependency to the pom.xml file.

Collection Matchers

Collections often have more complex testing needs. For example testing the size of a collection, testing for one or more elements in a collection, their ordering, etc. The Hamcrest collection matchers are designed to support the needs of testing collections in unit tests.
Let’s write a new test class and use the Hamcrest collection matchers.

CollectionMatchersTest

In the test class above, we initialized the Set collection of MatchersDemo with few strings in a @Before method. Then, we wrote the following assertions:

  • Line 23: We wrote a assertThat with hasSize to test the size of the collection.
  • Line 32: We used hasItems to test for multiple items in the collection. To test for a single item, you can use hasItem.
  • Line 41: We used containsInAnyOrder to test that all items in the collection match the expected items, in any order. If you want to test for all items in the same order, use the stricter contains.

Number Matchers

I particularly find the Hamcrest number matchers useful to test floating point calculations that provide accurate approximations, but not exact results. The assertion assertThat(2.32 * 3, equalTo(6.96)); will fail because the actual result is not what we expect (6.96). By, looking at the failure message we will understand the reason.

As you can notice, the actual value is different from what we expected. To test for such floating point calculations, there is a closeTo matcher method that we will cover now.

NumberMatchersTest

In Line 20 of the test class above, we used closeTo to test for the result of the floatingPointMultiplication() method under test. The closeTo method matches if an examined double value is equal to the first parameter value, within a range of +/- error specified by the second parameter. We also wrote an assertThat with greaterThan in Line 26 to check if the actual value returned by the method under test is greater than the specified value 6.0.

Some other number matcher methods are greaterThanOrEqualTo, lessThan, and lessThanOrEqualTo. As their names are self-explanatory, I won’t explain them further.

Text Comparison Matchers

We did some text comparisons with the core matchers on the toConcatedUpperCase() method earlier in this post. But, for more flexibility, let’s look at some specific text comparison matchers.

TextComparisionMatchersTest.java

In Line 21 and Line 27 we used equalToIgnoringCase and equalToIgnoringWhiteSpace to test for string equality while ignoring casing and white spaces respectively. In Line 33 we used stringContainsInOrder to test that the actual result contains the specified strings in the same sequence.

The Hamcrest matchers library is big. In this post we looked at few of them. But more importantly, we learned how unit tests are done the real way – Hamcrest matchers.

Summary

As you saw, JUnit’s assertThat combined with Hamcrest matchers has much better functionality. But by saying so, the old assert methods are here to stay. If you are running the old assert methods present in existing test code, you may continue doing so. But, if you plan to write new test code, consider using the Hamcrest matchers. They are more readable – as the JUnit release note says “This syntax allows you to think in terms of subject, verb, object– assert that x is 3”. Also, you will realize the full benefits when your test fails during complex testing. The detailed failure message will point you in the right direction at a very less, or no time at all.

During unit testing enterprise application code using the Spring Framework, you can unleash the potentials of the Hamcrest matchers. Beside the regular assertions, you can use the Hamcrest bean matchers to test properties of Spring beans. You can also use matchers to test whether a view name contains a specific string in Spring MVC, test responses from mock objects. You’ll find the versatility of the Hamcrest matchers very beneficial when writing unit tests in JUnit.

1
Share
, ,

In the first part of the series on unit testing with JUnit, we looked at creating unit tests both using Maven and IntelliJ. In this post, we will look at some core unit testing concepts and apply those using JUnit constructs. We will learn about assertions, JUnit 4 annotations, and test suites.

JUnit Assertions

Assertions, or simply asserts provide programmers a way to validate the intended behavior of code. For example, through an assertion you can check whether a method returns the expected value for a given set of parameters or a method correctly sets up some instance or class variables. When you run the test, the assertion executes. If the method under test behaves exactly as you specified in the assertion, your test passes. Otherwise, an AssertionError is thrown.

JUnit provides support for assertions through a set of assert methods in the org.junit.Assert class. Before we start using them, let’s have a quick overview of the Arrange, Act, Assert (AAA) pattern. This pattern is the recommended way to write unit test methods where you divide a method into three sections, each with a specific purpose:

  • Arrange: Initialize objects and set up input data for the method under test.
  • Act: Invoke the method under test passing the arranged parameters.
  • Assert: Verify that the method under test behaves as expected. This is where you write an assertion method.

Here is a Java class we will be writing some JUnit unit tests to test.

EmployeeEmail.java

In the EmployeeEmail class above, we wrote an addEmployeeEmailId() method that first checks whether an email ID is in valid format, and then adds it to a Map implementation. The isValidEmailId() method performs the email validation using a regular expression. We also wrote a getEmployeeEmailId() method to return an email ID from the Map, given a key.

To test the EmployeeEmail class, we will create a test class, EmployeeEmailTest and add test methods to it. Here, remember that the number of test methods to add and what they should do depends on the behavior of the EmployeeEmail class under test – not on the number of methods in it.

To start with, we will test that the getEmployeeEmailId() method returns true for a valid email ID and false for an invalid one with two test methods.

In both the test methods above, we separated the test code into the AAA sections. In the first test method, we used the assertTrue() method as we expect isValidEmailId() to return true for the email ID, [email protected]. We also want to test that isValidEmailId() returns false for an invalid email ID. For that, we wrote the second test method and used assertFalse().

Couple of things to observe here. In both the assertion methods, we passed a String parameter as the identifying message for an assertion error. It’s common for programmers to set this message to describe the condition that should be met. Instead, to be meaningful, this message should describe what’s wrong if the condition isn’t met.

Also, you might be thinking “Why two separate test methods instead of a single method with both the assert methods?” Having multiple assert methods in a single test method will not cause any errors in tests, and you will frequently encounter such test methods. But a good rule to follow is: “Proper unit tests should fail for exactly one reason”, which sounds similar to the Single Responsibility Principle.  In a failed test method having multiple assertions, more effort is required to determine which assertion failed. Also, it is not guaranteed that all of the assertions took place. For an unchecked exception, the assertions after the exception will not execute and JUnit proceeds to the next test method. Therefore, it is generally a best practice to use one assertion per test method.

With the basics in place, let’s write the complete test class and use the following assertions:

  • assertEquals() and assertNotEquals(): Tests whether two primitives/objects are equal or not. In addition to the string message passed as the first parameter, these methods accept the expected value as the second parameter and the actual value as the third parameter- an important ordering commonly misused.
  • assertNull() and assertNotNull(): Tests whether an object is null or not null.
  • assertSame() and assertNotSame(): Tests whether two object references point to the same object or don’t.

EmployeeEmailTest.java

In the EmployeeEmailTest class above:

  • Line 38: We used assertEquals() to test the collection size after adding two elements to it through addEmployeeEmailId().
  • Line 50: We used assertNotEquals() to test that the collection does not allow duplicate keys added through addEmployeeEmailId().
  • Line 62: We used assertNotNull() to test that getEmployeeEmailId() does not return null for an email ID present in the collection.
  • Line 74: We used assertNull() to test that getEmployeeEmailId() returns null for an email ID not present in the collection.
  • Line 89: We used assertSame() to test that two collection references point to the same collection object after assigning one to the other through the = operator.
  • Line 103: We used assertNotSame() to test that two collection references are not pointing to the same object.

When we run the test in IntelliJ, the output is:
Output in IntelliJ

As you can see from the output, all the tests passed as expected.

Note: The order in which JUnit executes test methods is not guaranteed, so don’t count on it.

If you go back and look into the test class, you will notice several lines of code in the Arrange part being repeated across the test methods. Ideally, they should be in a single place and get executed before each test. We can achieve this through the use of JUnit annotations, which we will look into next.

JUnit Annotations

You can use JUnit Annotations, introduced in JUnit 4, to mark and configure test methods. We have already used the @Test annotation to mark public void methods as test methods. When JUnit encounters a method annotated with @Test, it constructs a new instance of the class, and then invokes the method. We can optionally provide a timeout parameter to @Test to specify a time measured in milliseconds. If the test method takes longer to execute than the specified time, the test fails. This is particularly useful when you test against performance in terms of time. This code marks a method as a test method and sets the timeout to 100 milliseconds.

Another important use of the @Test annotation is to test for exceptions. Suppose for a condition, a code throws an exception. We can use the @Test annotation to test whether the code indeed throws the exception when the condition is met. This code checks whether the getEmployeeEmailId() method throws an exception of type IllegalArgumentException when a non-String value is passed to it.

In addition to the @Test annotation, the other annotations are:

  • @Before: Causes a method to run before each test method of the class. You typically use this annotation to allocate resource, setup common initialization code, and load configuration files that the test methods require.
  • @After: Causes a method to run after each test method of the class. This method is guaranteed to run even if a @Before or @Test method throws an exception. Use this annotation to clean up initialization code and release any resource allocations done in @Before.
  • @BeforeClass: Causes a static method to run once and only once before any of the test methods in the class. This is useful in situations where you need to set up computationally expensive resources, say a server connection, a database, or even managing an embedded server for testing. As an example, instead of starting a server for each @Test method, start it once in a @BeforeClass method for all the tests in the class.
  • @AfterClass: Causes a static method to run once after all the test methods in the class completes. This method is guaranteed to run even if a @BeforeClass or @Test method throws an exception. Use this method to free one time resource initialization done in @BeforeClass.
  • @Ignore: Causes a test method to be ignored by JUnit. This can be useful when you have a complicated piece of code that is in transition, and you might want to temporarily disable some tests till that code is ready. Test runners of most IDEs report @Ignore tests as reminders during each test runs. This is essentially to mark tests as “there are things to be done”, which otherwise you might forget if you comment out the test method or remove the @Test annotation.

Here is an example of using all the JUnit annotations.

EmployeeEmailAnnotationsTest.java

The output on running the test in IntelliJ is:

JUnit Output in IntelliJ

JUnit Test Suites

If you have large numbers of test classes for different functional areas or modules, you can structure them into test suites. JUnit Test Suites are containers of test classes and gives you finer control over what order your test classes are executed in. JUnit provides org.junit.runners.Suite, a class that runs a group of test classes.
The code to create a test suite is:

EmployeeEmailTestSuite.java

In the test suite class above, we wrote two annotations: @RunWith and @SuiteClasses. The @RunWith annotation instructs JUnit to use the Suite runner class and @SuiteClasses specifies the classes and their order that the Suite runner class should run. The test suite class is itself empty and acts only as a placeholder for the annotations.

The output on executing the test suite in IntelliJ is.

JUnit test suite Output in IntelliJ

Summary

JUnit Assertions not only make your code stable but also force you to think differently and think through different scenarios, which ultimately helps you to become better programmers. By understanding the purpose of different assertions and using them properly, testing becomes effective. But the question is “How many asserts per test method?”. It all comes down to the complexity of the method under test. For a method with multiple conditional statements, asserting the outcome for each condition should be done, while for a method performing a simple string manipulation, a single assertion should do. When developing unit tests with JUnit, it is considered a best practice that each test method is testing a specific condition, which will often lead to one assert per test method. Its not uncommon for a method under test to be associated with multiple test methods.
One assertion I have not covered in this post is assertThat(). It’s an important JUnit assertion which I will cover it in my next post on JUnit.

Unit Testing with the Spring Framework

While doing Enterprise Application Development with the Spring Framework and unit testing your code, you will be using lots of assertions. In addition to asserting the regular method behaviors, you will assert whether Spring beans are injected as expected by the Spring application context, whether dependencies between Spring beans are correctly maintained, and so on. While creating those tests ensure that they run fast, especially when testing is integrated in the build cycle. You will keep building your application as you code, and so you obviously won’t want your build to wait for a long running test to complete. If you do have such long running tests, put them in a separate test suite.

0
Share
, ,

Unit testing is the first level of testing software where you write test code that executes a specific functionality in the code to be tested. In most cases, you as a programmer are responsible to deliver unit tested code. The objective is to check if the unit of the software, for example a public method of a class under test, behaves as expected and/or returns the expected data. Unit tests are not done on the production system but as isolated units. If the unit under test have external dependencies, such as an external data source or a Web service, the dependencies are replaced with a test implementation or a mock object created using a test framework. Unit testing is not the only type and it alone cannot handle all testing aspects. Other types of testing, such as integration and functional testing have their own roles in testing software.

In this series of posts we will focus on unit testing with JUnit – one of the most popular framework to test Java code. In this post, we will start by creating and executing a basic unit test, and then in further posts move to specific unit testing aspects.

The core JUnit framework comes in a single JAR file, which you can download, point the classpath to it, and then create and run tests. But in this post, we will learn how to perform unit testing in the real programmer’s way. We will start with Maven, and then move to IntelliJ.

Unit Testing with Maven

You’ve likely heard Maven being referred as a build tool. But, in addition to its capability to build deployable artifacts from source code, Maven provides a number of features for managing the software development life cycle. Unit testing is one such feature, which is incorporated as a test phase in the Maven build lifecycle.

Without going into Maven in depth, let’s start our first JUnit test with Maven.

    1. Download and install Maven if you haven’t done yet.
    2. Open up a command prompt (Windows) or a terminal (*uix or Mac), browse to a working directory to setup the project, and execute the following command.

The preceding archetype:generate command uses the maven-archetype-quickstart template to create a basic Maven project containing a pom.xml file, a App.java class, and a AppTest.java test class in the following directory structure.

In the directory structure above, the pom.xml file, also known as the Maven configuration file is the heart of a Maven project. It is where you define your project configurations – specifically the dependencies of your project. For example, as our project depends on JUnit, we need to declare it as a dependency in the pom.xml file. Although a JUnit dependency will already be present by default, we will update it to point to the latest JUnit version. This is how our final pom.xml file will look like.

pom.xml

Now that we have set up a basic Java class, a test class, and the pom.xml configuration, we can run a unit test.

    1. Execute the mvn test command from the working directory.

This command will run the default AppTest class that Maven generated for us with the following output.

We have executed a JUnit test using Maven. This test passed, but hardly provides any value yet. We will next move to using the IntelliJ IDE to write and execute a more comprehensive test.

Unit Testing in IntelliJ

Using IntelliJ, you can easily create, run, and debug unit tests. Among several other unit testing frameworks, IntelliJ provides built-in support for JUnit. In IntelliJ, you can create a JUnit test class with a click and navigate quickly between test classes and their corresponding target classes to debug test errors. A very useful unit testing feature in IntelliJ is code coverage. With this feature, you can view the exact percentage of methods and even lines of code covered by unit tests in your project.

Let’s import our existing Maven project to IntelliJ and do some unit testing.

Import Maven Project to IntelliJ

If you don’t have IntelliJ installed, download and install the free Community Edition or the 30-day trial of Ultimate Edition from the official website. Once you are done, perform the following steps:

    1. Open IntelliJ.
    2. On the Welcome to IntelliJ IDEA window, click Import Project.

Click Import project

    1. In the Select File or Directory to Import dialog box, browse to the working directory of the Maven project and select the pom.xml file.

Select pom.xml

    1. Click the OK button.
    2. In the Import Project from Maven dialog box that appears, select the Import Maven projects automatically checkbox to synchronize changes between the Maven and InteliiJ projects each time the pom.xml file changes.

Select the Import Maven projects automatically checkbox

    1. Click the Next button through a few more dialog boxes, accepting the default settings, and finally click Finish. The Project window of IntelliJ displays the project structure.

Thct Structure in the IntelliJ Project Windowe Proje

  1. Double click App in the Project window to open it in the code editor.
  2. Replace the default code of the App class with this code.

App.java

In the code above we wrote a concatAndConvertString() method in the App class that accepts two String parameters. The method first concatenates the strings and converts the result to uppercase before returning it.

We will next add a test class to test the concatAndConvertString() method.

Add a Test Class

Let’s go through the steps to add a test class in IntelliJ from scratch.

    1. Delete the default AppTest class from the Project window.
    2. In the Project window create a directory with the name test under main, We will use the test directory to keep the test code separated from the application code.
    3. Right-click test and select Mark Directory As→Test Sources Root.

select Mark Directory As, and then Test Sources Root.

    1. In the code editor where the App class is open, press Shift+F10 and select Create New Test.

Select Create New Test

    1. In the Create Test dialog box that appears, select the jUnit4 radio button and the check box corresponding to the concatAndConvertString() method that we will test.

Select jUnit4 radio button and the method that we will test

  1. Click the OK button. JUnit creates the AppTest class with a testConcatAndConvertString() method decorated with the @Test annotation. This annotation tells JUnit to run the method as a test case. In the test method, we will write the code to test the concatAndConvertString() method of App.

AppTest.java

In Line 12 of the example above, we called the assertEquals() method, which is one of the several JUnit assertion methods. This overloaded method checks whether two String objects are equal. If they are not, the method throws an AssertionError. In our example, we called the assertEquals() method by passing the expected string value (HELLOWORLD) as the first parameter and the actual value that the concatAndConvertString() method returns as the second parameter.

Run the Unit Test

To run the test, select Run AppTest from the Run menu of IntelliJ or press Shift+F10. The Run window displays the test result. A green highlight box indicates that the test completed without any failure.

Green highlight box indicating a passed test

To know how test failures are reported, change the value of the expectedValue variable to HelloWorld and press Shift+F10. The Run dialog box displays a red progress bar to indicate the test failure along with a comparison failure message.

Red Progress Bar Indicating a Failed Test

Revert the the expectedValue variable to its original value before you close IntelliJ.

Summary

At this point, if you are thinking Why not just use System.out.println() for unit testing? Then you are thinking wrong. Inserting System.out.println() for debugging into code is undesirable because it requires manually scanning the output, every time the program is run, to ensure that the code is doing what’s expected. Imagine doing this in an enterprise application having hundreds and thousands of lines of code. On the other hand, unit tests examine the code’s behavior on runtime from the client’s point of view. This provides better insight on what might happen when the software is released.

Whenever you are writing code, you should be writing unit tests too. Writing unit tests will catch programming errors and improve the quality of your code. Many professional developers advocate doing Test Driven Development (TDD), where you write your unit tests before you write the application code.

Either way if you write your unit tests before or after you write the application code, the unit tests become a valuable asset for instrumenting your code. As the code base grows, you can refactor things as needed, and have more confidence that your changes will not have unintended consequences (ie where you change one thing, and accidently break something else).

In part 2 of my tutorial series on unit testing with JUnit I’ll take a deeper look at JUnit Assertions, JUnit Annotations, and JUnit Test Suites.

Unit Testing with the Spring Framework

Testing is an integral part of Enterprise Application Development process with the Spring Framework. The architecture of the Spring Framework lends itself to modular code and easier unit testing.  Spring provides testing support through the TestContext Framework that abstracts the underlying testing framework, such as JUnit and TestNG. You can use it by setting SpringJUnit4ClassRunner.class as the value for the @RunWith annotation. This tells Spring to use the test runner of TestContext instead of JUnit’s built in test runner. I’ve written a more in depth post about testing Spring applications with JUnit here.

1
Share