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:

@Entity
@Table(name = "Products")
public class Product {

    @Id
    @GeneratedValue
    private int id;

    @Size(min = 3, max = 20)
    private String name;

    @Size(min = 3, max = 20)
    private String countryOfOrigin;

    @Override
    public String toString() {
    return String.format("%s from %s",
    		this.name, this.countryOfOrigin);
    }

    ...

}

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:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>  {
}

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.

@RunWith(SpringRunner.class)
@ContextConfiguration(classes= {DataJpaConfiguration.class })
@DataJpaTest
public class ProductControllerTests {

	@Autowired TestEntityManager entityManager;

	...
}

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:

@Test
public void twoProductsRetrieved_afterAddingTwo() {
	Product product1 = new Product("Brie", "France");
	Product product2 = new Product("Parmigiano", "Italy");

	entityManager.persist(product1);
	entityManager.persist(product2);
	entityManager.flush();

	// controller queries the JPA repository internally
	String productDescription = controller.getAllProductDescriptions();

	assertEquals("{Brie from France,Parmigiano from Italy}",
			productDescription);
}

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:

@Configuration
public class JdbcTestConfiguration {

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource datasource ) {
		return new JdbcTemplate(dataSource());
	}

	@Bean
	public DataSource dataSource() {
		return DataSourceBuilder.create()
				.driverClassName("org.h2.Driver")
				.url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1")
				.username("sa")
				.password("sa")
				.build();
			}
}

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:

@Repository
public class ItemRepository {

	@Autowired JdbcTemplate jdbcTemplate;

	public void addItem(Item item) {
				String name = item.getName();
				Int itemValue = item.getValue();
				jdbcTemplate.update(
					"INSERT INTO items(name, value) VALUES(?,?)"
					, name, itemValue);
	}
}

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:

@Test
public void itemIsAddedByController() {
	Item item = new Item("Item1", 2);
	//The controller calls ItemRepository's addItem method
	controller.addItem(item);

	assertEquals(2, itemRepository.findByName("Item1").getItemValue());
}

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:

@Repository
public class ItemRepository {

	@Autowired JdbcTemplate jdbcTemplate;

	public void addItem(Item item) {
		String name = item.getName();
		Int itemValue = item.getValue();
		jdbcTemplate.update(
		"INSERT INTO items(name, value) VALUES(?,?)"
		, name, itemValue);
	}

	public Item findByName(String name) {
		Item item = jdbcTemplate.queryForObject(
				"select * from items where name=?",
				new Object[] {name},
				new BeanPropertyRowMapper<Item>(Item.class)
				);
		return item;
	}
}

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):

@Repository
public class TestItemRepository extends ItemRepository {

	public Item findByName(String name) {
		Item item = jdbcTemplate.queryForObject(
				"select * from items where name=?",
				new Object[] {name},
				new BeanPropertyRowMapper<Item>(Item.class)
				);
		return item;
	}
}

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

@Test
public void itemIsAddedByController_WithTestItemRepository() {
	Item item = new Item("Item1", 2);
	//The controller calls ItemRepository's addItem method
	controller.addItem(item);

	TestItemRepository testRepository = (TestItemRepository) itemRepository;
	assertEquals(2, testRepository.findByName("Item1").getValue());
}

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:

@Autowired JdbcTemplate jdbcTemplate;

@Test
public void itemIsAddedByController_UsingJdbcTemplateToQuery() {
	Item item = new Item("Item1", 2);
	//The controller calls ItemRepository's addItem method
	controller.addItem(item); 

	Item addedItem = jdbcTemplate.queryForObject(
			"select * from items where name=?", 
			new Object[] {"Item1"},
			new BeanPropertyRowMapper<Item>(Item.class)
			);
	
	assertEquals(2, addedItem.getValue());
}

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.

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {TestConfiguration.class})
@Sql(scripts = "classpath:CreateSchema.sql",
	executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:DeleteSchema.sql",
	executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class AppTests{
  ...
}

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:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MockPublisherMockBeanTests {

	@MockBean Publisher mockPublisher;

	@Test
	public void firstTest() {
		mockPublisher.publish();
		Mockito.verify(mockPublisher).publish();
	}

	@Test
	public void secondTest() {
		verify(mockPublisher, never()).publish();
	}
}

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:

@MockBean(answer=Answers.RETURNS_DEEP_STUBS) 
Publisher mockPublisher;

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.

@MockBean(reset=MockReset.NONE) 
Publisher mockPublisher;

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:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = { MockPublisherConfiguration.class })
public class MockPublisherResetTests {

	@Autowired Publisher mockPublisher;

	@Before
	public void setup() {
		Mockito.reset(mockPublisher);
	}

	@Test
	public void firstTest() {
		mockPublisher.publish();
		Mockito.verify(mockPublisher).publish();
	}

	@Test
	public void secondTest() {
		verify(mockPublisher, never()).publish();
	}
}

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:

@Configuration
public class MockPublisherConfiguration {

	@Bean
	public Publisher mockPublisher() {
		Publisher mockPublisher = mock(Publisher.class);
                doThrow(RuntimeException.class).when(mockPublisher)
                        .publishWithError();
		return mockPublisher;
	}
}

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:

@Configuration
public class MockPublisherConfiguration {

	@Bean
	public Publisher mockPublisher() {
		Publisher mockPublisher = mock(Publisher.class);
		return mockPublisher;
	}
}

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

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = { MockPublisherConfiguration.class })
public class MockPublisherTests {

	@Autowired Publisher mockPublisher;

	@Test
	public void firstTest() {
		mockPublisher.publish();
		Mockito.verify(mockPublisher).publish();
	}

	@Test
	public void secondTest() {
		verify(mockPublisher, never()).publish();
	}
}

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:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = { MockPublisherConfiguration.class })
public class MockPublisherTests {

	@Autowired ApplicationContext context;
	Publisher mockPublisher;

	@Before
	public void setup() {
		mockPublisher = context.getBean(Publisher.class);
	}

	@Test
	public void firstTest() {
		mockPublisher.publish();
		Mockito.verify(mockPublisher).publish();
	}

	@Test
	public void secondTest() {
		verify(mockPublisher, never()).publish();
	}
}

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:

@Configuration
public class MockPublisherConfiguration {

	@Bean
	@Scope("prototype")
	public Publisher mockPublisher() {
		Publisher mockPublisher = mock(Publisher.class);
		return mockPublisher;
	}
}

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
Controllers IControllers IIConsumer tests

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:

@Configuration
public class AppConfiguration {

	@Bean
	public Logger logger() {
		return new Logger();
	}
}

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.

@Profile("hacker")
@Configuration
@Import(AppConfiguration.class)
public class TestConfiguration {

	@Primary
	@Bean
	public Logger faultyLogger() {
		return new FaultyLogger();
	}

  ...

}

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:

@SpringBootTest
@ContextConfiguration(classes= {HackerConfiguration.class})
@ActiveProfiles("hacker")
public class HackingTests {

    @Autowired Logger logger;

    @Test(expected = RuntimeException.class)
    public void faultyLogger_throws() {
        logger.Write("Should throw")
    }
}

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
Controllers IControllers IIConsumer tests

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:

@Autowired JdbcTemplate jdbcTemplate;

@Test
public void verificationTest() {
      assertNotNull(jdbcTemplate);
}

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
Controllers IControllers IIConsumer tests

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.

@Configuration
public class PrinterMainConfiguration {

  @Bean
  public Printer laserPrinter() {
    return new LaserPrinter();
  }

  @Bean
  @Primary
  public Printer dotMatrixPrinter() {
    return new DotMatrixPrinter();
  }
}

When Spring sees a class with:

@Autowired Printer printer;

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:

@Configuration
public class PrinterMainConfiguration {

  @Bean
  public Printer laserPrinter() {
    return new LaserPrinter();
  }
}

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:

@Configuration
@Import(PrinterMainConfiguration.class)
public class PrinterTestConfiguration {

  @Bean
  @Primary
  public Printer mockPrinter() {
    return Mockito.mock(Printer.class);
  }
}

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
Controllers IControllers IIConsumer tests

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:

@ContextConfiguration(classes= {OurServiceConfiguration.class, DatabaseConfiguration.class, ...})
public class OurService {
  ...
}

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

@Configuration
@Import(DatabaseConfiguration.class)
public class OurServiceConfiguration {
  ...
}

And then at the service class:

@ContextConfiguration(OurServiceConfiguration.class)
public class OurService {
  ...
}

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.

@Configuration
public class MainControllerConfiguration {
  @Bean
  public OurController controller() {
    return new OurController();
  }
}

@Configuration
@Import(MainControllerConfiguration.class)
public class ControllerTestConfiguration {

  @Bean
  public OurDependency mockDependency() {
    OurDependency mock = Mockito.mock(OurDependency.class);
    when(mock.isActive()).thenReturn(true);
    return mock;
  }
}

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:

@SpringBootTest
@ContextConfiguration(ControllerTestConfiguration.class)
public class OurControllerTests {

  @Autowired OurController controller;
  @Autowired OurDependency mockDependency;

  ...
}

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.