GCP Datastore : Integration Testing using Spring Boot
In this article, I'll show you how you can create an in-memory datastore used by your tests.
Configuring a Datastore Profile Testing
Spring 3.1 introduced the bean definition profiles which can help us implementing an appropriate configuration for testing datastore staff. So, we'll have a specific profile for the GCP Datastore environment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vedrax.company.config; | |
import com.vedrax.tester.ITester; | |
import com.vedrax.tester.impl.GCPDatastoreTester; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.ComponentScan; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.context.annotation.Profile; | |
/** | |
* | |
* @author remypenchenat | |
*/ | |
@Configuration | |
@ComponentScan(basePackages = {"com.vedrax"}) | |
@Profile("datastoreTest") | |
public class ServiceTestConfig { | |
@Bean(name = "datastoreTester") | |
public ITester datastoreTester() { | |
ITester tester = new GCPDatastoreTester(); | |
return tester; | |
} | |
} |
Basically, the
GCPDatastoreTester()
class helps us saving data to the local in-memory datastore.Custom Annotation
In order to support the population of test data for each test case, we will create an annotation in which you can add the JSON file name as argument. In the test execution listener, we'll check for the existence of the annotation and load the data accordingly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vedrax.tester; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
/** | |
* | |
* @author remypenchenat | |
*/ | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.METHOD) | |
public @interface DataSets { | |
String json() default ""; | |
} |
We'll use the annotation as follow :
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@DataSets(json = "myTestingDataSet.json") | |
@Test | |
public void testCreateCompany() { | |
//something to test | |
} |
Implementing Custom TestExecutionListener
The
Process :
As you can see above we override only the
TestExecutionListener
interface in the spring-test
module defines a listener API for intercepting the events of the test case execution. Process :
- We check for the existence of the annotation
- we will create an instance of
LocalServiceTestHelper
withLocalDatastoreServiceTestConfig
which handles all the GCP environment setup before testing. - Load and insert data using the
setDataset()
method.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vedrax.company.config; | |
import com.vedrax.tester.DataSets; | |
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; | |
import com.google.appengine.tools.development.testing.LocalServiceTestHelper; | |
import org.springframework.test.context.TestContext; | |
import org.springframework.test.context.TestExecutionListener; | |
import com.vedrax.tester.ITester; | |
/** | |
* | |
* @author remypenchenat | |
*/ | |
public class ServiceTestExecutionListener implements TestExecutionListener { | |
private final LocalServiceTestHelper helper | |
= new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); | |
private ITester datastoreTester; | |
@Override | |
public void afterTestMethod(TestContext testCtx) throws Exception { | |
helper.tearDown(); | |
} | |
@Override | |
public void beforeTestMethod(TestContext testCtx) throws Exception { | |
DataSets datasetAnnotation = testCtx.getTestMethod().getAnnotation(DataSets.class); | |
helper.setUp(); | |
if (datasetAnnotation == null) { | |
return; | |
} | |
String datasetName = datasetAnnotation.json(); | |
if (!datasetName.equals("")) { | |
//see configuration file for the bean | |
datastoreTester = (ITester) testCtx.getApplicationContext().getBean("datastoreTester"); | |
datastoreTester.setDataSet(datasetName); | |
} | |
} | |
} |
As you can see above we override only the
beforeTestMethod()
and afterTestMethod()
in which we call also the setUp()
and tearDown()
methods.How To Use
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vedrax.company.service; | |
import com.vedrax.company.config.ServiceTestConfig; | |
import com.vedrax.company.config.ServiceTestExecutionListener; | |
import com.vedrax.company.domain.Company; | |
import com.vedrax.tester.DataSets; | |
import java.util.Optional; | |
import org.junit.Test; | |
import static org.junit.Assert.*; | |
import org.junit.runner.RunWith; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import org.springframework.test.context.ActiveProfiles; | |
import org.springframework.test.context.TestExecutionListeners; | |
import org.springframework.test.context.junit4.SpringRunner; | |
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; | |
/** | |
* | |
* @author remypenchenat | |
*/ | |
@RunWith(SpringRunner.class) | |
@SpringBootTest(classes = {ServiceTestConfig.class}) | |
@TestExecutionListeners({ServiceTestExecutionListener.class, | |
DependencyInjectionTestExecutionListener.class}) | |
@ActiveProfiles("datastoreTest") | |
public class CompanyServiceTest { | |
@Autowired | |
private CompanyService companyService; | |
@DataSets(json = "companyTest1.json") | |
@Test | |
public void testCreateCompany() { | |
Optional<Company> companyOpt = companyService.getCompanyByDomainName("vedrax.com"); | |
assertTrue(companyOpt.isPresent()); | |
} | |
} |
Because we use our own test execution listener, all default listeners are overridden. In order for the DI to work properly we've also added
DependencyInjectionTestExecutionListener.
Comments
Post a Comment