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

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

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

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

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

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.

Integration Testing with Spring – Profiles

Gil Zilberfeld explains Spring files and how they relate to 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

Today we’re going to discuss Spring profiles. Profiles are sort of a “meta configuration” in Spring. They are like a configuration for a configuration.

We usually set profiles up with either a role (“dev”, “tester”, “devops”, etc.) or with an environment (“dev”, “test”, “integration”, etc.). They are “meta” in the sense that there’s a bunch of configurations that are related to developers, and then there are a different subset for testers, for example. Those bean groups can be combined, or intersect, based on the needed configuration. We can set profiles on a configuration class or a single bean, and basically tell Spring whether to load them or not, based on the active configuration.

Let’s say we have this configuration class in production:

As you’ve probably guessed, it creates a real Logger. Now, in our integration test class we can use (as we saw in the @Primary post) a different configuration.

When we use the TestConfiguration it will (normally) inject a FaultyLogger. But since we’ve put a @Profile on it, the beans in the TestConfiguration will be loaded only if the active profile when the tests run is Hacker.

The test class might look like that:

The @ActiveProfiles annotation tells the integration test class under which profile it runs (obviously). So everything works fine.

If we remove the @ActiveProfiles annotation, we’ll get a NoSuchBeanDefinition exception. That is because the “hacker” profile was not activated, and therefore the TestConfiguration was not loaded. And since the test does not import the AppConfiguration directly, it doesn’t find a Logger bean.

If we would import the AppConfiguration, or the TestConfiguration but without the @ActiveProfile set to hacker, the test will fail, because it will inject the normal logger.

By now, you’re probably asking: so what?

Profile: What is it good for?

In integration testing? Not much.

Like many features in Spring, they are useful in their context. But for integration testing, we’re looking for something else.
We want tests that do not fail or pass on their context. An active profile is such a context. So we want to limit the impact of something like that on our tests.

Let’s say the application we’re testing has some hacker-context related features we want to test. It’s written using Spring’s Profile features, and so these features must be run under an active Hacker profile. That’s cool.

How would we go about testing that? We’d create a test class (or two) that use the @ActiveProfiles annotation, like we did in our example. In there we’ll put all the Hacker profile related tests. By putting all these tests together in one (or two) places, we’ve already separated them from the rest of their tests. Which helps the deterministic aspect we’re looking for.

Come to think about it, that’s how we usually separate test classes from each other, right? based on features or context?

Using Profiles doesn’t really help us in testing, it just supports Spring app code that’s written with Profile support. Which is again, cool.

There’s a minor scenario where using @ActiveProfiles with different profiles might be helpful: If the integration tests are already mashed up together without profile separation. If something goes wrong, you might want to play with @ActiveProfiles to switch profiles and debug what’s happening.

However, if you’re doing that, you might want spend more time on separating the tests into different test classes which run under different profiles.

Integration Testing With Spring – Avoiding Problems

Gil Zilberfeld describes how to avoid problems created with bean injection 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

We already know that Spring is complex. And we’ve just scratched the surface. One of the problem with complex frameworks is that they do so much, that when something goes wrong they are not that smart about analyzing the situation. And that really gets in our way of doing actual programming.

For example, when a Bean doesn’t load, it can tell you that there was a problem loading it, but what exactly happened is hard to find on the stack. Especially if one dependency called another, and we are five levels deep in bean dependency. You can spend a lot of time here, so here are a couple of tips on how not to get there.

Don’t do anything important in the constructor

Beans usually create objects and return them. Those objects have constructors, that can do many things. But you shouldn’t.

You can store things in variables. But avoid any code in the constructor of your injected object. While we’re at it, definitely don’t have any static initializer that can create some “invisible” damage in the stack model of “who runs first”. If you need to initialize something in the object, add an Initialize method. I know it’s not Object Oriented in clean form, but we’re building components that integrate with a massive system, and it’s hard enough when it works.

Add a verification test in your test class

Remember the contextLoads() test that’s created for us in the Spring Boot test folder? What it does, as the name implies, is verify the ApplicationContext loads correctly. If it doesn’t, there’s not much point in running the rest of tests, right?

This test is like our canary in the coal mine. We can take this idea further.

When you add your own tests, especially when relying on automated wiring that Spring does from explicit configuration code, or god help us, implicitly and automatically. A simple test that checks that:

We’ll get to JdbcTemplate in a future post, but suffice it to say it can be injected automatically to different objects in the system. Having this test in place localizes the error, and makes it easier to investigate what went wrong and fix it.

Whenever you’re starting a new test class, it’s advisable to have something like this around. And don’t throw it away, keep it around for stormy Spring weather. Winter is coming.

Integration Testing with Spring – Primary Beans

Gil Zilberfeld explains the Primary bean annotation for using in 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

Last time we talked about breaking down configurations into small parts, so we can work in smaller chunks, that make our integration tests easier. This leads us to the next subject.

Remember that question we had? How does Spring know which bean to inject if it has multiple factory method for the same type? Let’s answer this in a single configuration class.

Let’s say in our main configuration class there are two beans of type Printer. We already talked about how to select a bean using its name. But Spring gives us a way to specify a default bean: @Primary.

When Spring sees a class with:

It will inject the DotMatrixPrinter one, because that bean has the @Primary annotation.

How can we use @Primary in integration tests?

@Primary becomes very effective in tests, because we might want to override the bean that is already defined in production. Let’s say our Printer type is configured in the app configuration like this:

So Printer will always result in injecting a LaserPrinter.  However, in our test we want to inject a mock Printer.

So lets create a test configuration:

Now, remember that when we import a configuration, there’s no “override” operation between the configurations, it’s adding the beans from the imported configuration to the importing one.

What happens when we use the PrinterTestConfiguration? The bean mockPrinter we added with @Primary tells Spring to load itself “over” the laserPrinter production one. The only trick is to not specify a @Primary in the main configuration.

Integration Testing With Spring – Organizing Configurations

Gil Zilberfeld explains how to organize Spring configurations for integration testing
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

We’ve talked about nested configurations. Now, let’s move on to using imported configurations in a smart way.

Let’s say in the main app (as in “production”), I have a whole lot of beans I need to inject.

The thing not to do (although you might be tempted) is dump them all into one big configuration class. It might feel like “just one more bean wouldn’t hurt”. But it will. Maybe not today, maybe not tomorrow. But soon, and for the rest of your project life.

We might end up with tens, even hundreds of beans, so using a big class may be convenient in answering “where do I put my next shiny new bean”, but at the same time, we probably want to segment them better .

Separate Configurations

One way to do this is by service or functionality: Each service has its own configuration. With micro-services this is crucial. When we update the service, we update the configuration too, and deploy them as a single unit.

There are some things that are more cross-cutting, and less service related, like general database connectivity initialization beans. We can put those in a separate configuration class. Then for the application we can use @ContextConfiguration to load the right configuration classes:

As alternative, we can also  @Import at the configuration class level:

And then at the service class:

While both of these work, we conceal the fact that we’re using the DatabaseConfiguration in the service. This can come back to bite us later, as we’re carrying invisible baggage. So let’s stick with the first one.

And Testing Configurations?

When breaking down a full configuration into separate parts, we get a couple of advantages for testing:

  • We load only what we need for the test, not the entire system configuration
  • Run time (including load time) can seriously decrease
  • Less things that can fail our tests
  • Test setup becomes less of a hassle.

Here’s an alternative to the nested configuration example, only this time, the configuration is in a separate class.

In this case, the MainControllerConfiguration class is in the production part of the application, and the ControllerTestConfiguration is in the test part (obviously). The latter imports the former, which can save us time in creating objects we already have defined, and don’t need to change.

So when we use the ControllerTestConfiguration in a test it looks like this:

Notice that this way, we keep the link to the configuration through our ControllerTestConfiguration importer. Only it decides what to import or override. The test class is oblivious. This is quite cool.

So what’s “Overriding” a bean? That’s next.

Integration Testing with Spring – Nested Configurations

Gil Zilberfeld discusses spring features for integration testing, this time nested configurations
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

Let’s dig deeper into configurations for integration tests in Spring.

We’ll start with a nested integration context example, for a class (that might be a test class, because that’s our main focus). While we can use @Import, if we set up one-time configuration, it could be nested in the test class. Here’s an example:

Let’s take a look at what we have here. Our test class, and code-under-test will inject two @Autowired objects. The first one is the controller. Its @Bean is configured in another configuration class, called ControllerConfiguration. This configuration is imported by the nested configuration in the test class.

The NestedConfiguration class declares a bean that creates and initializes a mock, which is probably a different implementation from the main app implementation. Also, the reason for using a one-time configuration is having the initialization different than other configurations, used by other test classes.

We need to remember a couple of things of how Spring works:

  • The nested configuration class has to be static. While usually not a problem, it loads differently than regular configuration classes.
  • If we use an @ContextConfiguration on the test class (not @Import on the nested configuration class, as in the example), it will override the nested configuration. That can be confusing and can cause hilarious (or disastrous) consequences.

The cool thing about a nested configuration is that it’s right there in the test class that needs it, so no need to look for it elsewhere. That’s a readability plus.

However, nested configurations are not re-usable, and we would probably want to reuse a single configuration for multiple test classes. So, unless nested configurations are your default way of working, it’s probably better to use non-nested ones. They should be used in very special circumstances for very special initialization.

Next, we’ll look at managing and organizing configurations for production and tests.