“Spock” Talk at ExpoQA 2019, Madrid

Gil Zilberfeld describes his upcoming talk "Spock" on the unit testing framework that allows clean code and readable tests.
Standard

“Why do we need another testing framework”?

Next week, I’m giving a brand new talk at the expoQA conference in Madrid. It’s on Spock – the unit testing framework, not the Starfleet commander. The first question I need to answer is – isn’t JUnit enough?

Sure, there’s TestNG, but other than that – why do we need another unit test framework in the Java world?

The simple answer is – you probably don’t. Spock doesn’t do much beyond JUnit. Actually behind the facade, it does everything that JUnit + Mockito do.

But if we take a step back, we can ask – why do we need other languages on the JVM on top of Java?

When you take a look at Groovy (or Kotlin), you start seeing a difference between how code is written and can be read. You write less code for your program to work, and the language makes the code more readable, and therefore easier to understand and maintain. It’s clean code 101, but it starts even before you write a single line.

Let’s take it back to JUnit and Spock. Can we write readable JUnit tests? Sure, but it requires some extra boiler plate code we need to hide, and a a whole lot of discipline. Spock tests, written in Groovy, are already lighter in code, but also have the label keywords (given, when, then, expect) to make the tests more readable.

Yes, we still need to invest in good names and abstractions. This doesn’t go away. But the experience is more fluid.

Here’s an example of the Spock tests I’m going to show at expoQA.

See, Avengers (and starfleet) make everything cooler.

See you in Madrid!

“Dependency Injection” Talk – The Slides

Gil Zilberfeld explains how testability is impacted by developer decisions like dependency injection
Standard

Had a great fun at the meetup, as always. And the fun part was that my message resonated a lot stronger after the lightning talk before mine, where the speaker talked about how the developers chose a technique that made testing hard, because of un-observable and uncontrollable components. So dependency injection is part of clean code, and testability and all that. But there are a lot more decisions, on architecture design and coding, that affect testers, users and other stakeholders.

Remember kids: You maybe “just testers”, but you need to be part of the development conversation. Code is too important to leave to developers.

Here are the slides:

Dependency Injection Talk at the Test.IL Meetup

Gil Zilberfeld is talking about Clean Code principle dependency injection in the TestIL meetup
Standard
Gil Zilberfeld is talking about Clean Code principle dependency injection in the TestIL meetup

Next Wednesday, 22 May ’19, I’m giving a lightning talk about a very important topic: Dependency Injection.

Most developers know what it is, and now it’s time for testers to learn about it too – And that’s why this talk is at the Test-IL meetup.

Dependency injection is a wide subject, from frameworks to principles. How it is a direct descendant of the SOLID principles (and therefore how using it as part of clean code principles). How using it in TDD, unit testing and integration testing allows us to replace or mock different parts of the system and check their consumers in isolation.

But there’s a deeper insight when talking about testability which doesn’t even concern code. And I’m going to reveal it in the talk.

Of course don’t miss the other great talks as well.
You can register to the meetup here.

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:

public class Client {

	@Autowired RestTemplate template;

	public String getAllItems() {
		try {
			ResponseEntity<String> result =
				template.getForEntity("/item/" , String.class);
			return result.getBody();
		}
		catch (HttpClientErrorException e) {
			if (e.getStatusCode()== HttpStatus.BAD_REQUEST)
				throw new ItemNotFoundException();
		}
		return "";
	}
}

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:

@Configuration
public class TestClientConfiguration {

	@Bean
	public Client client() {
		return new Client();
	}

	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

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

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {TestClientConfiguration.class})
public class ClientTests {

	@Autowired RestTemplate template;
	@Autowired Client	client;

	MockRestServiceServer mockServer;

	@Before
	public void setup() {
		mockServer = MockRestServiceServer.createServer(template);
	}
	...
}

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

@RunWith(SpringRunner.class)
@AutoConfigureMockRestServiceServer
@SpringBootTest
@ContextConfiguration(classes= {TestClientConfiguration.class})
public class ClientTests {

    @Autowired MockRestServiceServer mockServer;
		...
}

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.

@Test
public void get_ServerCalledCorrectly() {
	mockServer
			.expect(once(), requestTo("/item/"))
			.andRespond(
					withSuccess("", MediaType.TEXT_PLAIN));
	client.getAllItems();
	mockServer.verify();
}

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:

@Test (expected = ItemNotFoundException.class)
public void add_ThrowsOnError() {
	mockServer.expect(once(), requestTo("/item/"))
		.andRespond(withBadRequest());
	client.getAllItems();
}

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:

@RestController()
@RequestMapping("/items/")
public class ItemController {

	@Autowired ItemRepository itemRepository;

	@GetMapping(value ="/")
	public ResponseEntity<String> getAllItems() {
		Integer numberOfItems = itemRepository.findTotal();
		if (numberOfItems == 0)
			return new ResponseEntity<String>("Error", HttpStatus.SERVICE_UNAVAILABLE);
		else {

			String result =numberOfItems.toString() + " Items";
			return new ResponseEntity<String> (result , HttpStatus.OK);
		}
	}

	@PostMapping(value = "add/")
	public ResponseEntity<?> addItem(@RequestBody String itemName) {
		Item item = new Item(itemName, 1);
		itemRepository.addItem(item);
		return new ResponseEntity(HttpStatus.OK);
	}
}

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.

@Autowired WebApplicationContext wac;
private MockMvc mockMvc;

@Before
public void setup() {
	mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
			.build();
	controller.Reset();
}

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:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {TestControllerConfiguration.class})
@AutoConfigureMockMvc
public class ControllerTests {

	@Autowired MockMvc mockMvc;
	...
}

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

@Test
public void whenNoItemsAvailable_thenGetReturnsAnErrorCode() throws Exception {
	mockMvc.perform(get("/items/"))
		.andExpect(status().isServiceUnavailable());
}

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:

@Test
public void whenItemsAreAdded_thenGetReturnsNumberOfItems() throws Exception {

	mockMvc.perform(post("/items/add/")
			.content(asJsonString("Item1"))
			.contentType("application/json"))
			.andExpect(status().isOk());

	mockMvc.perform(post("/items/add/")
			.content(asJsonString("Item2"))
			.contentType("application/json"))
			.andExpect(status().isOk());

	MvcResult result =  mockMvc.perform(get("/items/"))
			.andExpect(status().isOk())
			.andReturn();

	assertEquals("2 Items",result.getResponse().getContentAsString());
}

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

private String asJsonString(Object obj) {
		try {
			return new ObjectMapper().writeValueAsString(obj);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
}

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:

@Test
public void api_whenItemsAreAdded_thenGetReturnsNumberOfItems() throws Exception {
	addItem("Item1");
	addItem("Item2");
	assertEquals("2 Items",getTotal());
}

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:

@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.