Integration Testing with Spring: Consumers

Gil Zilberfeld explains how to mock API calls for integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

We’ve been through a lot. We’ve covered all kinds of flows to be tested on the server side. We’ve seen how to check flows from the controller down to the database, combined with injecting different mocks and services. We’ve got one more thing: What happens if my controller (or any other component) makes an external API call. I may want to mock that call. Does String help?

Here’s our client. It calls the ItemController from last time, through its API:

We need to supply the RestTemplate through configuration . Just create one, it’s created in the context of the WebApplication, and therefore the web container that Spring runs. The RestTemplate is very helpful in posting and getting, and making HTTP API calls. If something bad happens, it throws an exception we can check and do something about it.

So if we want to check the logic of our client in an integration test, we would want to mock the API call. We can, as we’ve seen before, inject a mock RestTemplate, and tell it what to return. That’s kids stuff. But then everybody will make their own mock, and that’s a waste.

So, of course, we get a Spring solution. It’s got the sexy name MockRestServiceServer. Yeah, I know, but it does what it says on the label.

In order to create one, we need to remember that the RestTemplate we use is connected to the test server. when we run the integration test. We need to have a shared RestTemplate between our client-under-test, and the integration test itself, and Spring happily injects one for us. It’s still a regular RestTemplate:

Once we’ve got a configuration, we can use it in our integration test class:

Then we build our MockRestServiceServer manually, using our RestTemplate. Or if we’re lazy, Spring gives as an annotation to inject the MockRestServiceServer automatically, so we don’t have to build it ourselves (or the RestTemplate):

Now that we got mock what are we gonna do with it? For the first integration test, let’s configure it to return a value that doesn’t trigger an exception.

Just like good ol’ EasyMock, but at the API level, we can set expectations, behavior and verify those callse.

For the second integration test, we’ll ask the mock to return a bad request answer that will result in throwing the exception:

That’s pretty nifty. We can control dependent services in our integration tests.

We can have the client call the the server, if it makes sense. For example, if we can set up the database and configure it, we can check the integration between the services.

Remember, the longer the sequence, the bigger the setup, and the chance of the flow breaking on something else rises too. There are cases though when mocking is necessary, if we don’t control the server, of if the server returns a different result every time. Think an API that returns the global time or weather. These are hardly repeatable. And controlling the weather is even harder.

Integration Testing with Spring – Controllers II

Gil Zilberfeld explains considerations for Spring controller integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

We’ve gone over what we needed from Spring to test a controller. And Spring is about to give it to us.

Here’s a controller:

This controller has GET and POST APIs. It has some logic in it (although this is something we probably don’t want. We’d rather push that into our application logic layer). It also calls a Repository (it doesn’t matter which type, JDCB or JPA.) While we can just inject it into our integration tests, and call its methods directly, we’d rather call the APIs as a URI.

Remember: what we’ve wanted all along was a standalone server, that from its entry points onward, runs our code. That server can be called with an API and return an HTTP code and payload.

Spring comes with a handy class called MvcTestController. It is basically a web server that lives for the duration of the integration test run. It is limited to exposing only the API of the controllers-under-test, so not all the application loads. Our wish has comes true, with a bonus.

In order to create it, we have a couple of ways. The first one is to use the WebApplicationContext, which Spring creates and injects automatically.

The mockMvc is created for each integration test. But even that maybe too much, so the good people of Spring thought of us lazy bastards, and gave us an annotation for our integration test class:

@AutoConfigureMockMvc basically injects mockMvc with less coding. So what can this thing do?

TestMockMvc comes packed with a fluent interface to do all the HTTP operations (here we do a HTTP GET) and asserts if we got the right status. It can do a lot more though: We can pass in headers, request bodies, authorization data. And we can return all kind of statuses and check the response body. Here’s another integration test:

Here’s the JSON serialization method, used to wrap the strings:

As you can see, the TestMockMvc allows us to operate and check the controller as if it was called from the outside. While you might be tempted to write all your tests like this, remember that the longer the test path, the integration test can fail for many reasons.

There’s another downside: integration tests tend to get longer and longer, and therefore hard to read and maintain. Which makes it very important to extract those repeated calls into methods and leave the integration test like this:

Much better, right? That’s how we like our integration tests.

So that’s the TestMockMvc. What I’d like next is to mock a full API call. Can I do that? Of course I can. Next time.

Integration Testing with Spring – Controllers I

Gil Zilberfeld explains considerations for Spring controller integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

In Spring, Controllers allow external consumers to use REST APIs to call into the application logic. This is very convenient, since we don’t need to write a whole translation level. Spring controllers give us full access to the request parameters and headers, and allows us to create a response and return a status.

Up until now, we’ve looked at how to test components under the hood of APIs. Namely, our integration tests created classes-under-test either with “new“, or let Spring inject them directly into the test classes. While those integration tests can give us a nice coverage, it’s not how they will be called in the application.

In our integration tests, we want to go up a level, and try to call the controllers and go in, sometimes deep into the database. Such scenarios gives us the confidence our API works as expected. However, there are some testing considerations.

  • Spring configures a web server and runs the full application. In our integration tests, we’d like to not to operate the real application. So we’d like to call a different server, maybe with a different address and port.
  • In production mode, everything is up in and running. But in testing mode, we may need just a subset of “things”. Only the beans that interest us, and only the data that’s needed, for testing that one API.

Sometimes we forget to ask a simple question: What’s the value of testing through the API?

Until now, we’ve tested this path:

Gil Zilberfeld explains considerations for integration tests in Spring
Integration tests until now

For the same scenario, we now want to check the following path:

Gil Zilberfeld explains considerations for integration tests in Spring
Extending our tests

Yes, we can go through the controller, and check that Spring works as expected. But, we’re kind of assuming it does, right?

It’s nice to know the integration fails, but then, there’s not much we can do about it. And we have a different kind of problems.

The next layer is the Controller logic. It can have its own logic, but a better design would be to act only as a gateway for calling into the Application logic. From this point on, our other integration tests take over.

The value in testing the whole path, is to give us confidence that “everything plays together”. However, it should not be the default mode of our integration tests. As we’re stepping up the testing pyramid, longer scenarios should be kept to a minimum. They are expensive to set up and harder to identify problems than short test scenarios.

That means that from design point of view, we should keep the Controller logic to a minimum as well. In most cases, it should transfer Request information in and Response information out. Then, we can just unit test the Controller logic, or write “unit” (Spring integration and mocking the other end) tests just for it before it calls into the Application logic, concentrating on the controller code.

So to summarize, we need the API tests, but we should have less of them than other integration tests and unit tests.

Next time, we’ll see how Spring helps us with Controller integration tests that go all the way to the database.

Integration Testing with Spring – Data III

Gil Zilberfeld explains the data integration tests in Spring
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

Hibernate and JPA have done a lot for developers in minimizing the code needed to write database applications. Spring Data does a lot of magic behind the code for us, and we’d like to take advantage of that magic.

Let’s start with the basics: In JPA we’re mapping objects to tables in the database, or rather, inform Spring what tables we want for the objects.

So if our object is like this:

Spring understands we want a table called Products, where each row is mapped to a Product instance. The ID is the index of the table, and it’s going to be auto-generated. But Spring needs more information – we need a repository object to access that data:

Doesn’t look like much, right? But this empty interface does a lot. JpaRepository is full of data access methods, and with Spring magic, it creates all the code needed for us to use these objects as if we’ve created them and the database manually.

We also need to configure the data source, which can be configured either through application.properties file or Spring’s DataSource APIs.

The main @Bean we need to configure in our configuration class is the LocalContainerEntityManagerFactoryBean, which does all the connections, loading and initialization for our database. That includes where to find repository classes, and setting the data source. I’m not going in depth here, since I want to move on to integration testing features.

Test, Please

Spring Boot comes with a handy @DataJpaTest annotation that extends @SpringBootTest. It tells Spring that our integration test is not just a regular integration test, but requires help from the Spring JPA gods, and it comes in the shape of a bean called TestEntityManager.

Spring creates this object for us, no need for configuration, so we can us it in our integration tests. What for, you ask? Remember we had the issue of checking the result of the added item in the database we had with JDBC? Our TestEntityManager is the JdbcTemplate counterpart. It helps us put stuff in the database, or help us query it.

(You may also notice we didn’t use the @SQL annotation this time to build the database, because Spring builds the table for us, by identifying the @Entity annotation on our class.)

Now we can write the integration test:

We use the TestEntityManager to push data into the database, and then check that the data was returned correctly from the controller.

Data integration tests are very useful in terms of making sure that our code runs correctly up to the database. Spring, with a wee help from in-memory databases, helps solve a lot of issues of isolation and setup, as well as focusing on writing what we need in the integration tests. Cool stuff.

So far we’ve checked classes we’ve created and invoked. Next we’re going to go a step up: Spring Controllers and how to test them.

Integration Testing with Spring – Data II

Gil Zilberfeld explains the data integration tests in Spring
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

Today we’ll tackle JDBC options for testing in Spring.

Before we start, there’s one more annotation to learn from Spring: @Repository. We put it on a class to tell Spring that this is a data class, talking to a data source. A @Repository is basically a component or a bean. Repositories are the Data Access Objects we use to talk to the database.

Our DAO uses JdbcTemplate which is Spring main gateway to the database. It is configured with a data source to talk to a specific database. For example, a configuration for data source will look like this:

Obviously, for integration tests, we want to work with a different data source than production. Using Spring handy DataSourceBuilder class, giving it different database properties, we can use a different database for each integration test class. Here, I’m using H2 in-memory database basic configuration. All the tricks we’ve already discussed with configurations apply here.

A repository object looks like this:

This repository uses the JdbcTemplate we create in the integration test configuration above, so when it loads, it is already connected to an existing database. It is ready to work – at least to add items.

The missing link here, is obviously the table creation. We can use the @SQL scripts we explored last time to create it before the integration test runs. Now we have a way to talk to the database before, during and after the integration test. We have achieved full database integration. Almost.

Suppose I want to write an integration test to check that data was actually added to the database. Something like this:

Integration tests usually don’t work directly against the repositories , they usually operate on their users, like the controller in our example. But we might need access for certain operations for the integration tests.

In our case, our repository doesn’t have a findByName method, that will help us check if data was actually persisted. Another example would be a deleteAllItems method, I would want to run at the end of each integration test to clean the table.

We have two options: The first is to define additional methods on the DAO for the integration test. For example:

Another option is to use Spring injection flexibility. Instead of using the production ItemRepository, we can derive a class from it TestItemRepository. It can have more accessibility (but we need to make the base’s JdbcTemplate protected to use it in the derived repository):

And use our JdbcTestConfiguration to inject it instead. It does require casting in the integration test body:

We added more accessibility, in return for some testing code. That’s a fair price.

The final option is inject the JdbcTemplate bean directly and use it to get the data we need:

Right. Enough JDBC. Next time: JPA.

Integration Testing with Spring – Data I

Gil Zilberfeld explains the data integration tests in Spring
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

We’ve discussed what Spring can do for us in terms of integration tests so far, but nothing says more integration than database integration.

First, let’s define what we want in our integration tests, before we describe how the wonderful people of Spring and Spring Boot help us achieve it.

In our unit tests, we have complete isolation. In our integration tests, we want to have as much isolation as possible, given we’re already using Spring as a foundation. We’ve already discussed how to inject mocks, but the next step is to work with a database.

But working with a database ain’t cheap. We need an always-available database, whenever and where-ever we run the tests, so they won’t fail on any issue other than the actual code.

While it can be a shared database (like a dev-team database), there are conditions to be met. All kinds of integration tests are going to run at all times against this database. So it has to be up-to-date, in terms of both schema and data for all integration tests. Since we don’t control which integration test runs what and when, that creates a problem, if we update the schema, for example. Or, if one integration test relies on data that another integration test has deleted. Whenever there’s an integrity issue like this, our integration tests will fail, and it will take time to see what the problem is, and then we’ll curse ourselves that we didn’t use a private database.

So a private database it is. Sure, Oracle or Microsoft don’t give out those licenses away, but let’s say we’ll get around that. Even with our own database, with no other people interfering, there is still a data integrity issue. We’ll still need to manage everything ourselves.

It would be nice to have a throwaway database, wouldn’t it?. One, that once the integration tests have run, we don’t care what happens there, because there would be no footprint left.

I have another wish from my database. If we have a couple of features, one that accesses the Students table and the other needs the Teachers table, we don’t need both tables for testing each single feature separately. Loading the database with just the specific data for each integration test is easier to manage, much more than a whole database for all integration tests.

So our ultimate testing database is a disposable, private, test-specific, and as long as my wish list is still open, as fast as possible, we’d also like the same sweet feedback we get from unit tests.

A man can dream, no?

The good news is we can have all that and more.

First of all, meet H2, an in-memory (or embedded) relational database that’s pretty much built for integration testing. It’s equivalent to any relational database. It is private, because it runs in the same process as our code.
It is fast, because it doesn’t write to the disk. And once the process of our integration tests completes, it goes down without any trace (because data is not persisted).

H2 is not part of Spring, but you can select it to be included as part of a Spring Boot project. Once you got it there, we’re ready to go. Almost.

Using the database

Spring Boot does an awesome job in creating that “out of the box” feeling, including configuring the database. We can use the “spring.datasource.*” properties in the application.properties file, specifically the one that we keep in src/test/resources folder – the one that will run at test time, rather than the one in production. Or we can specify our data source in code, as we’ll see next time.

Spring Boot will start the embedded database server based on those properties. It creates a database for us. We can use that database for the purpose of the integration tests, which now can fill it. That’s where Spring comes with another awesome shortcut: the @SQL annotation for the test class.

As you can see, the @SQL annotation lets us run SQL scripts for creating a schema and filling in data (we can do whatever we want, really). We can also specify when to run these scripts, using the executionPhase member. The initialization scripts are executed to create the schema (at least), and some common data, will be there waiting for the integration tests. The scripts can be conveniently placed at the src/test/resources folder.

Why should we delete the data/schema at the end? After all, we said the database is disposable. While true, the lifecycle of the database is the whole integration test run. It maybe that after this test class, another test class will run, with different database needs. So let’s be good person-scouts and leave the grounds clean.

So that’s the setup stage. Now we need to actually to talk with the database in the integration tests themselves. We’ll split our discussion to JDCB and JPA flavors, but that’s next time.

Integration Testing with Spring – Mocking III

Gil Zilberfeld explains how to using mocking in Spring integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

In the last couple of posts we’ve discussed how we can use mocks in Spring integration tests. We’ve covered using a bean “prototype” and resetting mocks.

There is one more option.

The MockBean

By now you’re probably thinking: Those Spring guys must understand the need for proper mocking in integration tests. Didn’t they think about this stuff?

Well, of course they did. It’s called a MockBean.

A MockBean bean is injected by Spring, for testing purposes, for every integration test. And we get a nice shiny unscratched instance for each test. We don’t even need to handle auto-wiring, create a configuration and reset anything. Spring does all this magic for us.

This time, our integration test class looks like this:

Notice, we are not importing any configuration (or creating a bean). Instead of @Autowired instance we’ve got a @MockBean. That’s all it takes. Spring does the rest. Note that since there’s no configuration, all the set up should be done in the test class itself.

Everything you wanted to know about MockBeans but were afraid to ask

A @MockBean overrides by default a pre-configured bean. That means that if we import a Configuration with the same type of bean, the @MockBean is injected as a mock, overriding the factory method in Configuration class.

Also, remember that a @MockBean is a mock like any other Mockito mock, and therefore has defaults. If you want to change those (for example, change to a preferred deep-stubbing option, you can use it like this:

Finally, we know that a @MockBean resets after every test by default. If you want to change this option, you can use the reset before every test, or not at all.

Although that last one kinda misses the point.

On one hand, using a @MockBean gives good isolation from other test classes, since, as we’ve seen last time, it may be erased by an erroneous reset.

On the other hand, that may also be a limitation – if there are multiple setups we need on it, not just for our integration test class. Imagine we need to create mock object tree for multiple test classes to consume as a starting point, and each one customizes it for its purposes. In that case, the way to go is probably a prototype, and being very careful with resets.

That’s all about mocking for now. Next time: Data stuff.

Integration Testing with Spring – Mocking II

Gil Zilberfeld explains how to using mocking in Spring integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

Last time we talked about the mocking injection issue with Spring, and discussed the option of using Spring’s “prototype” injection. We figured out, this solution is not for everyone.

What can we do with a regular, Spring singleton mock?

Resetting mocks

Mockito has a nifty little method called reset. As you can guess, it resets everything about a mock (or list of mocks). It resets all expectations, and in our case, method call tracking.

So, we can rewrite our Spring integration tests (using the original singleton configuration) like this:

The mockPublisher is @Autowired again, a singleton like in the original integration test class, and is injected once. Before each integration test, we reset the mock to start from scratch (we can also use @After for the same effect), and presto! Both integration tests pass.

In order for this method to work, we need to add a @Before method, and don’t forget call reset. It’s a bit manual, and if people forget it, they tests quickly fail and remind them.

However…

There’s a minor (read: huge) problem with this method. Since there’s only a singleton instance in our Spring application, reset resets the mock object for everyone who’s got a reference of it. Not just for our tests, but also for other consumers.

Suppose our configuration looked like this:

The doThrow behavior setting, that everyone should have enjoyed, is reset also whenever Mockito.reset is called. So even if our tests don’t use it, it may fail other tests as well that planned to use it.

So here’s a tip for you. When creating mocks in configuration classes, don’t don’t do anything but create them. Additional behaviors should be set only in the tests.

Now that we got this off our chests, there’s one more option we’ll explore next time: the humble MockBean.

Integration Testing with Spring – Mocking I

Gil Zilberfeld explains how to using mocking in Spring integration tests
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

Spring has some cool features, and Spring Boot doesn’t just pack JUnit into its packages, but also Mockito, so we can use mocking to our hearts content.

We just need to be careful in our integration tests. We need to be wary of…

The Bean Life-cycle

Remember the whole dependency injection power of Spring? So far, we’ve used the phrase “when the application loads”, to say when beans are injected. And that is mostly true (unless you programmatically refresh the beans, more on that later).

“When the application loads”, the application context loads, and with it the whole set of Configurations, which include beans. For example:

So far, so good. We’re injecting a mock. Now let’s look at our completely contrived test class that uses that bean.

Our tests check if the publish method was called. When we run it, the firstTest passes, and the secondTest fails. But why?

Our mockPublisher instance, used in both integration tests, is injected only once, when the test class instance is created. That’s the default injection mode in Spring, called “singleton” scope.

Using singletons is great in many use cases. However, we don’t want this behavior for mocking. We want mocks to be created every time from scratch, to lower the dependency between tests. In unit tests, we have full control on how to create any instance, so just recreating them in the setup or the test method is enough. With Spring integration tests it’s different.

So what can we do?

Using a prototype

When using “singleton” scope, the factory method is called once, and the instance is cached. Using “prototype” scope tells Spring that every time we need an instance, it should create a new instance, like we want from our mock.

Alas, in our integration tests, we have a single creation (or injection) for our mock. The factory method in the MockPublisherConfiguration is called only once.

But that’s because we’re using @Autowired. When the test class loads, Spring sees the @Autowired member and injects it, and that happens a only once. What if we get the bean programmatically, rather than through @Autowired?

Let’s change our test class a bit:

As you can see, the mockPublisher is no longer @Autowired. We’re injecting the Spring ApplicationContext, and use it before every test. The getBean method fetches the mockPublisher instance for every test. This alone doesn’t do the trick, though. Remember, once the factory method is called, the instance is cached. Now, let’s change the Configuration class:

We’ve added the “prototype” scope to our bean and presto – Both integration tests pass!

Now we get a different instance for every test. The second one’s publish method indeed does not get called.

It works! We’ve got a different mock for each test.

Here’s the bad news: Most people don’t even understand how Spring works, so getting hot and heavy with the ApplicationContext may be too much for them. Not you, of course, but other people.

So for them, next time we’ll look at other options for mocking in Spring integration tests.