spring boot
, ,

In my earlier Integration Testing with Spring and JUnit post, I discussed how to test a service bean facade with JUnit. I also posted a video, titled Testing Spring with JUnit on YouTube. In post, Spring Boot Web Application – Part 3 – Spring Data JPA, I showed how to test a Spring Data JPA repository of a Spring MVC application.

It has been more than a year now since I wrote these posts and posted the video. Since then, there’s been an exciting overhaul of testing support for Spring Boot MVC applications. The Spring Boot 1.4 release includes a number of exciting new testing features for our use.

In this post, I will look the new Spring MVC testing features. And I’ll show you how to put them to use.

Spring Boot 1.4 Testing Enhancements

In Spring Boot 1.3, there’s a lot of choices to write tests for a Spring MVC application. One option to set Spring MVC is shown in my earlier post here. In this post on Spring Boot 1.3, the @RunWith annotation with @ContextConfiguration is used to test for a business service façade, like this:

Another method I used in the post here is a combination of the @RunWith annotation with @SpringApplicationConfiguration to test a Spring Data JPA repository, like this:

There are several other approaches you can check in the official Spring blog here.

The testing approaches I used above are actually integration tests.  A pure unit test shouldn’t create and load Spring Context.

Spring Boot 1.4 replaces these various testing approaches that via a single @SpringBootTest annotation for regular integration tests.

Prior to Spring Boot 1.4, I found Spring was lacking a simplified unit testing approach. This is really no surprise. The Spring team is always creating. Spring and Spring Boot offers a number of testing options. Due to innvotation, the testing options have evolved over time. In Spring Boot 1.4, the Spring committers took some time to clean testing up. They gave us much simpler options to use for testing Spring Boot applications. For example, a simple approach to unit test a controller having @Autowired external services without having to load Spring Context was lacking. With Spring Boot 1.4, it’s now possible.

Another missing piece that Spring Boot 1.4 tackels, is the ability to test portions (slices) of code. This can be done without the need to fire up a server. And with out the need to load up the entire Spring Context. Spring Boot 1.4 does this through the new Test Slicing feature that is designed to set-up a narrow slice of the Spring Context. This makes testing single ‘slices’ much easier. You can now focus on testing specific slices of your application. For example:

For example:

  • MVC slice: Controller code through the @WebMvcTest annotation
  • JPA slice: Spring Data JPA repository code through the @DataJpaTest annotation
  • JSON slice: JSON serialization code through the @JsonTest annotation

This may not seem like much at first glance. But when you have a large application starting the Spring context in testing, it is time consuming. Context loads can really increase your build time.

Let’s start putting the new test features to use.

The Application Under Test

I wrote a series of posts on Spring MVC starting off from Spring Boot Web Application – Part 1 – Spring Initializer. In the last post of the series, Spring Boot Web Application – Part 4 – Spring MVC, I completed creating a Spring MVC application to perform Create, Read, Update, and Delete (CRUD) operations.

In this post, I’ll show you how to write tests for the controllers of the same Spring MVC application.

If you are new to Spring MVC, you should go through my series on Spring MVC starting here.

You can also download the source code of the application available on GitHub here to follow along this post.

It’s a pretty simple example of a Spring Boot MVC application consisting of the following primary components:

  • Product: The domain object, which is a JPA entity
  • IndexController: Returns the index.html Thymeleaf template for a GET request to the application root
  • ProductController: Contains number of actions methods that use ProductService to perform CRUD operations via the repository model
  • ProductRepository: A Spring Data JPA repository
  • ProductService: A business service façade interface
  • ProductServiceImpl: A business service façade implementation annotated with @Service

With the Spring Boot MVC application that will be under test in place, lets start by writing few tests for the controllers.

Maven Dependencies

The testing features we’re looking at were introduced in Spring Boot 1.4. The version of Spring Boot we’ll be using is 1.4.0.RELEASE.

Here is the complete Maven POM that we’ll use.

pom.xml

Unit Testing Spring MVC Controllers

MockMvc has been around since Spring 3.2. This providing a powerful way to mock Spring MVC for testing MVC web applications. Through MockMvc, you can send mock HTTP requests to a controller and test how the controller behaves without running the controller within a server. You can obtain a MockMvc instance through the following two methods of MockMvcBuilders:

  • standaloneSetup(): Registers one or more @Controller instances and allows programmatically configuring the Spring MVC infrastructure to build a MockMvc instance. This is similar to plain unit tests while also making it possible to focus tests around a single controller at a time.
  • webAppContextSetup(): Uses the fully initialized (refreshed) WebApplicationContext to build a MockMvc instance. This lets Spring load your controllers as well as their dependencies for a full-blown integration test.

Pro Tip: Whenever possible, I will try to use standaloneSetup() for my SpringMVC tests. Your tests will remain true unit tests and stay blazing fast!

This is the IndexController that we are going to test:

IndexController.java

For our purpose, we’re starting with standaloneSetup() to test this IndexController.

The test class is this.

IndexControllerTest.java

The test class above is a JUnit test. If you are new to JUnit, you should go through my series on unit testing with JUnit, starting from here. In the test class above, observe the new Spring Boot 1.4 test runner, named SpringRunner that we specified for @RunWith in Line 20. Under the hood, both SpringRunner and its predecessor SpringJUnit4ClassRunner are the same. SpringRunner is only the new name for SpringJUnit4ClassRunner – to just make it easy on the eyes.
In the @Before annotated method that runs before all @Test method, we programmatically constructed a MockMvc instance after registering the IndexController instance.

In the @Test method, we used the MockMvc instance to verify the following behavior of IndexController:

  • HTTP status code 200 is returned
  • The name of the returned view is index

Finally, by using andDo(print()), we get the following output on the console

Spring MockMvc IndexController Test Output

Testing the Spring MVC Slice

The unit test we wrote were for some basic expectations of the controller. Let’s write some more specific tests, this time to test ProductController. This time we’re going to use webAppContextSetup() to build MockMvc.

For a quick recap, the ProductController class is this.

ProductController.java

We will start by testing the behavior of ProductController.list() method. For a GET request to /product, we will perform the following verification:

  • The ProductService mock is not null
  • The HTTP status code 200 is returned
  • The returned content type is text/html;charset=UTF-8
  • The name of the returned view is products
  • The view contains the Spring Framework Guru string

Here is the test class.

ProductControllerTest.java

As we are testing the MVC slice of the application (testing whether the ProductController is working as expected), we used the @WebMvcTest annotation combined with @RunWith(SpringRunner.class).

As we planned to use webAppContextSetup() to build MockMvc, we @autowired WebApplicationContext in Line 6 – Line 7 to bring it into our test. Then in line 13, we passed WebApplicationContext as an argument to webAppContextSetup() to build the MockMvc instance.

Going back to the ProductController class under test, note that the controller class is @Autowired with ProductService. Therefore, we used the @MockBean annotation to define a Mockito mock for ProductService (Line 8 -Line 9) that will be passed to the controller. If you are new to mocking in unit tests, checkout my Mocking in Unit Tests with Mockito post.

Coming back to the test, in Line 17 we used the AssertJ library to assert that the ProductService mock is not null.

Note: Starting with Spring Boot 1.4, AssertJ comes out-of-the-box with Spring Boot to provide a fluent assertion API with a plan to replace JUnit’s  org.junit.Assert class.

From Line 19 – Line 23, it’s all about verifying our expectations. As you can see, a lot of static methods are being used in this test method, including static methods of MockMvcRequestBuilders ( get()), MockMvcResultMatchers ( status(), content(), and view()), MockMvcResultMatchers ( match()), and Hamcrest Matcher’s ( match()). The last two match() are similar and performs the same functions in our test. They exist together only to demonstrate the different approaches that can be used.

Our test method reads naturally. First it performs a

First it performs a GET request against /products. Then it expects that the request is successful ( isOk() asserts an HTTP 200 response code) and that the content type and name of the view is text/html;charset=UTF-8 and products respectively. Finally, it asserts that the view contains the Spring Framework Guru string.

When all the expectations pass, Line 24 prints the result out to the console.

The important thing to note here is that at no time is the application gets deployed to a server. The Tomcat container is not use. Instead the application runs within a mocked out Spring MVC to handle the HTTP request that we provided through the

Instead the application runs within a mocked out Spring MVC to handle the HTTP request that we provided through the MockMvc instance.

Here is the test result in the console.
Test output of ProductController.list()

The complete output of the test sent to console is this.

Testing Spring MVC Slice with @Autowired MockMvc

Now let’s test the behavior of showProduct() of ProductController. Instead of manually building MockMvc, we’ll use a @Autowired MockMvc in the test and let Spring create, configure, and provide a MockMvc for us.

This is how the test class now looks minus any @Test method implementations.

In the test class above, notice that we used the @Autowired annotation on MockMvc in Line 5 – Line 6 instead of building it manually.

An @Autowired MockMvc combined with @WebMvcTest(controllers = ProductController.class) gives us a fully configured MockMvc instance with Spring security configured to set up BASIC authentication.

At this point, if we run the ProductControllerTest.testList() test again, we’ll encounter an authentication error, like this.

We’re getting the 401 response because Spring Boot is auto-configuring Spring Security for us.

To disable the Spring Security auto-configuration, we can the MockMvc instance to disable security with @AutoConfigureMockMvc(secure=false) in Line 3.

Note, in the @Before method, we created and initialized a Product domain object that we will use in the @Test method.

The @Test method is this:

In the @Test method above:

  • Line 4: Performs an AssertJ assertion to test that the ProductService mock is not null.
  • Line 5: Uses Mockito to stub the getProductById() method on the ProductService mock to return the initialized Product instance
  • Line 8 to Line 15: Performs the following verifications for a GET request to product/{id}:
    • The HTTP status code 200 is returned
    • The name of the returned view is productshow
    • The view model contains a product attribute
    • The various properties of the product attribute matches against the values we used to initialize Product
  • Line 16: Returns the result as MvcResult
  • Line 19- Line 20: Uses AssertJ to assert that the content type of the response is
    text/html;charset=UTF-8
  • Line 22- Line 27: Uses JUnit assertions to assert that:
    • The response header that MvcResult returns as MockHttpServletResponse is not null
    • There is only one response header
    • The response header name is Content-Type
    • The response contains the Spring Framework Guru string
  • Line 29 -Line 30: Uses Mockito to verify that the getProductById() is called only once on the ProductService mock, and that no other methods of the ProductService mock are called during the test.

The complete test class is this:

ProductControllerTest.java

The complete output of the test sent to console is this:

Summary

The new @WebMVC used with MockBean allows creating powerful yet simple tests for your Spring MVC apps. Unlike the @SpringBootTest annotation, the @WebMvcTest annotation disables full auto-configuration. @WebMvcTest only auto-configures the Spring MVC infrastructure and limits scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Filter, WebMvcConfigurer, and HandlerMethodArgumentResolver beans.

When you use @WebMvcTest, regular @Component, @Service, or @Repository beans will not be scanned – an important point to differentiate @WebMvcTest from a full-blown @SpringBootTest.

If you’re looking to load your full application configuration and use MockMVC, you should consider @SpringBootTest combined with @AutoConfigureMockMvc rather than @WebMvcTest. I will cover it in an upcoming post of this Spring MVC testing series. I will also help you to explore more about mocking services and JPA repositories with @MockBean combined with @DataJpaTest and @WebMvcTest, and also how to unit test RESTful controller’s GETs and POSTs using MockMvc and @JsonTest.

4
Share
spring framework guru
, ,

Unit tests should be small tests (atomic), lightweight, and fast. However, an object under test might have dependencies on other objects. It might need to interact with a database, communicate with a mail server, or talk to a web service or a message queue. All these services might not be available during unit testing. Even if they are available, unit testing the object under test along with its dependencies can take unacceptable amount of time. What if?

  • The web service is not reachable.
  • The database is down for maintenance.
  • The message queue is heavy and slow.

These all defeat the whole purpose of unit tests being atomic, lightweight, and fast. We want unit tests to execute in a few milliseconds. If the unit tests are slow, your builds become slow, which affects the productivity of your development team. The solution is to use mocking, a way to provide test doubles for your classes being tested.

If you’ve been following the SOLID Principles of Object Oriented Programming, and using the Spring Framework for Dependency Injection, mocking becomes a natural solution for unit testing. You don’t really need a database connection. You just need an object that returns the expected result. If you’ve written tightly coupled code, you will have a difficult time using mocks. I’ve seen plenty of legacy code which could not be unit tested because it was so tightly coupled to other dependent objects. This untestable code did not follow the SOLID Principles of Object Oriented Programming, nor did it utilize Dependency Injection.

Mock Objects: Introduction

In unit test, a test double is a replacement of a dependent component (collaborator) of the object under test. A test double provides the same interface as of the collaborator. It may not be the complete interface, but for the functionality required for the test. Also, the test double does not have to behave exactly as the collaborator. The purpose is to mimic the collaborator to make the object under test think that it is actually using the collaborator.

Based on the role played during testing, there can be different types of test doubles, and mock object is one of them. Some other types are dummy object, fake object, and stub.

What makes a mock object different from the others is that it uses behavior verification. It means that the mock object verifies that it (the mock object) is being used correctly by the object under test. If the verification succeeds, it can be considered that the object under test will correctly use the real collaborator.

Free Spring Tutorial
Check out my FREE Introduction to Spring course!

The Test Scenario

For the test scenario, consider a product ordering service. A client interacts with a DAO to fulfill a product ordering process.

We will start with the Product domain object and the DAO interface, ProductDao.

Product.java

ProductDao.java

For the purpose of the example, I kept the Product class empty. But in real applications, it will typically be an entity with states having corresponding getter and setter methods, along with any implemented behaviors.

In the ProductDao interface, we declared two methods:

  • The getAvailableProducts() method returns the number of available quantity of a Product passed to it.
  • The orderProduct() places an order for a product.

The ProductService class that we will write next is what we are interested on the object under test.

ProductService.java

The ProductService class above is composed of ProductDao, which is initialized through a setter method. In the buy() method, we called getAvailableProducts() of ProductDao to check if sufficient quantity of the specified product is available. If not, an exception of type InsufficientProductsException is thrown. If sufficient quantity is available, we called the orderProduct() method of ProductDao.

What we now need is to unit test ProductService. But as you can see, ProductService is composed of ProductDao, whose implementations we don’t have yet. It can be a Spring Data JPA implementation retrieving data from a remote database, or an implementation that communicates with a Web service hosting a cloud-based repository – We don’t know. Even if we have an implementation, we will use it later during integration testing, one of the software testing type I wrote earlier on. But now, we are not interested on any external implementations in this unit test.

In unit tests, we should not to be bothered what the implementation is doing. What we want is to test that our ProductService is behaving as expected and that it is able to correctly use its collaborators. For that, we will mock ProductDao and Product using Mockito.

The ProductService class also throws a custom exception, InsufficientProductsException. The code of the exception class is this.

InsufficientProductsException.java

Using Mockito

Mockito is a mocking framework for unit tests written in Java. It is an open source framework available at github. You can use Mockito with JUnit to create and use mock objects during unit testing. To start using Mockito, download the JAR file and place it in your project class. If you are using Maven, you need to add its dependency in the pom.xml file, as shown below.

pom.xml

Once you have set up the required dependencies, you can start using Mockito. But, before we start any unit tests with mocks, let’s have a quick overview of the key mocking concepts.

Mock Object Creation

For our example, it’s apparent that we need to mock ProductDao and Product. The simplest way is through calls to the mock() method of the Mockito class. The nice thing about Mockito is that it allows creating mock objects of both interfaces and classes without forcing any explicit declarations.

MockCreationTest.java

An alternative way is to use the @Mock annotation. When you use it, you will need to initialize the mocks with a call to MockitoAnnotations.initMocks(this) or specify MockitoJUnitRunner as the JUnit test runner as @RunWith(MockitoJUnitRunner.class).

MockCreationAnnotationTest.java

Stubbing

Stubbing means simulating the behavior of a mock object’s method. We can stub a method on a mock object by setting up an expectation on the method invocation. For example, we can stub the getAvailableProducts() method of the ProductDao mock to return a specific value when the method is called.

In Line 4 of the code above, we are stubbing getAvailableProducts(product) of ProductDao to return 30. The when() method represents the trigger to start the stubbing and thenReturn() represents the action of the trigger – which in the example code is to return the value 30. In Line 5 with an assertion, we confirmed that the stubbing performed as expected.

Verifying

Our objective is to test ProductService, and unitl now we only mocked Product and ProductDao and stubbed getAvailableProducts() of ProductDao.

We now want to verify the behavior of the buy() method of ProductService. First, we want to verify whether it’s calling the orderProduct() of ProductDao with the required set of parameters.

In Line 6 we called the buy() method of ProductService that is under test. In Line 7, we verified that the orderProduct() method of the ProductDao mock get’s invoked with the expected set of parameters (that we passed to buy()).

Our test passed. But, not complete yet. We also want to verify:

  • Number of invocations done on a method: The buy() method invokes getAvailableProduct() at least once.
  • Sequence of Invocation: The buy() method first invokes getAvailableProduct(), and then orderProduct().
  • Exception verification: The buy() method fails with InsufficientProductsException if order quantity passed to it is more than the available quantity returned by getAvailableProduct().
  • Behavior during exception: The buy() method doesn’t invokes orderProduct() when InsufficientProductsException is thrown.

Here is the complete test code.

ProductServiceTest.java

I have already explained the initial code of the test class above. So we will start with Line 36 – Line 38 where we used the inOrder() method to verify the order of method invocation that the buy() method makes on ProductDao.

Then we wrote a purchaseWithInsufficientAvailableQuantity() test method to check whether an InsufficientProductsException gets thrown, as expected, when an order with quantity more than the available quantity is made. We also verified in Line 54 that if InsufficientProductsException gets thrown, the orderProduct() method is not invoked.

The output of the test is this.

Summary

Mocking in unit testing is extensively used in Enterprise Application Development with Spring. By using Mockito, you can replace the @Autowired components in the class you want to test with mock objects. You will be unit testing controllers by injecting mock services. You will also be setting up services to use mock DAOs to unit test the service layer. To unit test the DAO layer, you will mock the database APIs. The list is endless – It depends on the type of application you are working on and the object under test. If your following the Dependency Inversion Principle and using Dependency Injection, mocking becomes easy.

The Mockito library is a very large and mature mocking library. It is very popular to use for mocking objects in unit tests. Mockito is popular because it is easy to use, and very versatile. I wrote this post as just an introduction to mocking and Mockito. Checkout the official Mockito documentation to learn about all the capabilities of Mockito.

Free Spring Tutorial
Check out my FREE Introduction to Spring course!
4
Share
, ,

In this series on unit testing with JUnit, we learned several unit testing aspects and how to implement them with JUnit. We can summarize the series till now as:

  • Part 1: Creating a basic unit test both using Maven and IntelliJ
  • Part 2: Using assertions and annotations
  • Part 3: Using assertThat with Hamcrest matchers

In this post, we will learn about parameterized tests and theories.

JUnit Parameterized Tests

While testing, it’s common to execute a series of tests which differ only by input values and expected results. As an example, if you are testing a method that validates email IDs, you should test it with different email ID formats to check whether the validations are getting correctly done. But testing each email ID format separately, will result in duplicate or boilerplate code. It is better to abstract the email ID test into a single test method and provide it a list of all input values and expected results. JUnit supports this functionality through parameterized tests.

To see how parameterized test works, we’ll start with a class with two methods which we will put under test.

EmailIdUtility.java

The EmailIdUtility class above has two utility methods. The createEmailID() method accepts two String parameters and generates an email ID in a specific format. The format is simple – If you pass mark and doe as parameters to this method, it returns [email protected]. The second isValid() method accepts an email ID as a String, uses regular expression to validate it’s format, and returns the validation result.

We will first test the isValid() method with a parameterized test. JUnit runs a parameterized test with a special runner, Parameterized and we need to declare it with the @RuntWith annotation. In a parameterized test class, we declare instance variables corresponding to the number of inputs to the test and the output. As the isValid() method under test takes a single String parameter and returns a boolean, we declare two corresponding variables. For a parameterized test, we need to provide a constructor, which will initialize the variables. 

EmailIdValidatorTest.class

We also need to provide a public static method annotated with @Parameters annotation. This method will be used by the test runner to feed data into our tests.

The @Parameters annotated method above returns a collection of test data elements (which in turn are stored in an array). Test data elements are the different variations of the data, including the input as well as expected output needed by the test. The number of test data elements in each array must be the same with the number of parameters we declared in the constructor.

When the test runs, the runner instantiates the test class once for each set of parameters, passing the parameters to the constructor that we wrote. The constructor then initializes the instance variables we declared.

Notice the optional name attribute we wrote in the @Parameters annotation to identify the parameters being used in the test run. This attribute contains placeholders that are replaced at run time.

  • {index}: The current parameter index, starting from 0.
  • {0}, {1}, …: The first, second, and so on, parameter value. As an example, for the parameter {“[email protected]”, true}, then {0} = [email protected] and {1} = true.

Finally, we write the test method annotated with @Test. The complete code of the parameterized test is this.

EmailIdValidatorTest.java

The output on running the parameterized test in IntelliJ is this.

Parameterized Test Output

JUnit Theories

In a parameterized test, the test data elements are statically defined and you as the programmer are responsible for figuring out what data is needed for a particular range of tests. At times, you will likely want to make tests more generalized. Say, instead of testing for specific values, you might require to test for some wider range of acceptable input values. For this scenario, JUnit provides theories.

A theory is a special test method that a special JUnit runner (Theories) executes. To use the runner, annotate your test class with the @RunWith(Theories.class) annotation. The Theories runner executes a theory against several data inputs called data points. A theory is annotated with @Theory, but unlike normal @Test methods, a @Theory method has parameters. In order to fill these parameters with values, the Theories runner uses values of the data points having the same type.

There are two types of data points. You use them through the following two annotations:

  • @DataPoint: Annotates a field or method as a single data point. The value of the field or that the method returns will be used as a potential parameter for theories having the same type.
  • @DataPoints: Annotates an array or iterable-type field or method as a full array of data points. The values in the array or iterable will be used as potential parameters for theories having the same type. Use this annotation to avoid single data point fields cluttering your code.

Note: All data point fields and methods must be declared as public and static.

In the code example above, we annotated a String field with the @DataPoint annotation and a names() method that returns a String[] with the @DataPoints annotation.

Creating a JUnit Theory

Recall the createEmailID() method that we wrote earlier on this post – “The createEmailID() method accepts two String parameters and generates an email ID in a specific format.” A test theory that we can establish is “Provided stringA and stringB passed to createEmailID() are non-null, it will return an email ID containing both stringA and stringB ”. This is how we can represent the theory.

The testCreateEmailID() theory we wrote accepts two String parameters. At run time, the Theories runner will call testCreateEmailID() passing every possible combination of the data points we defined of type String. For example (mary,mary), (mary,first), (mary,second), and so on.

Assumptions

It’s very common for theories NOT to be valid for certain cases. You can exclude these from a test using assumptions, which basically means “don’t run this test if these conditions don’t apply“. In our theory, an assumption is that the parameters passed to the createEmailID() method under test are non-null values.

If an assumption fails, the data point is silently ignored. Programmatically, we add assumptions to theories through one of the many methods of the Assume class.
Here is our modified theory with assumptions.

In the code above, we used assumeNotNull because we assume that the parameters passed to createEmailID() are non-null values. Therefore, even if a null data point exists and the test runner passes it to our theory, the assumption will fail and the data point will be ignored.
The two assumeThat we wrote together performs exactly the same function as assumeNotNull. I have included them only for demonstrating the usage of assumeThat, which you can see is very similar to assertThat we covered in the earlier post.

The following is the complete code using a theory to test the createEmailID() method.

EmailIDCreatorTest.java

In the test class above, I have included null as a data point in the return statement of Line 23 for our assumptions and couple of System.out.println() statements to trace how parameters are passed to theories at run time.

Here is the output of the test in IntelliJ:
JUnit Theories Output

Also, here is the output I got while running the test with Maven for your review:

In the output above, notice that whenever a null value is being passed to the theory, the remaining part of the theory after assumeNotNull does not execute.

Summary

Parameterized tests in JUnit helps remove boiler plate test code and that saves time while writing test code. This is particularly useful during Enterprise Application Development with the Spring Framework. However, a common complaint is that when a parameterized test fails it’s very hard to see the parameters which caused it to fail. By properly naming the @Parameters annotation and great unit testing support that modern IDEs provide, such complains are quickly failing to hold grounds. Although theories are less commonly used, they are powerful instruments in any programmers test toolkit. Theories not only makes your tests more expressive but you will see how your test data becomes more independent of the code you’re testing. This will improve the quality of your code, since you’re more likely to hit edge cases, which you may have previously overlooked.

0
Share
, ,

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
spring framework guru
, ,

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
spring framework
, ,

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.