Configurable acceptance tests using Spring Boot
In a previous article, I have described how you could make your acceptance tests being delivered as an executable, so they can be executed at any time in your deployment pipeline.
Here, I will focus on the selection and configuration of tests according to the environment they run in. Typical use cases are:
- selection of test cases according to the environment
- configuration of connection parameters
Selection of tests
There are situations where I would not want my tests to run in very environment - for example, a test which actually updates the application should not run in production.
In order to achieve this, I'll transform the tests I created previously into injected tests, which I can control using the Spring profiles.
The @AcceptanceTestSuite
annotation declares an acceptance test class.
The @AcceptanceTest
allows to configure a test method to run or not in a given environment.
For example:
@AcceptanceTestSuite
class ACCBrowserBasic extends AcceptanceTestClient {
@Test
void 'Home page is accessible'() {
browser {
goTo HomePage, [:]
}
}
@Test
@AcceptanceTest(excludes = "production")
void 'Admin login'() {
browser { browser -> loginAsAdmin(browser) }
}
}
Now, if I run the acceptance tests with the option --context=production
, the Admin login test will not be executed while the Home page is accessible test will.
How does it work?
First of all, the JUnitAcceptanceRunner
defined in a previous post will be upgraded to detect automatically the tests being annotated with @AcceptanceTestSuite
:
// JUnit runtime
JUnitCore junit = new JUnitCore()
// XML reporting
XMLRunListener xmlRunListener = new XMLRunListener(System.out)
junit.addListener(xmlRunListener)
// Gets all the acceptance suites
def suites = applicationContext.getBeansWithAnnotation(AcceptanceTestSuite)
// Filters on classes
suites = suites.findAll { name, bean ->
def acceptanceTest = applicationContext.findAnnotationOnBean(name, AcceptanceTest)
return acceptanceTest == null || config.acceptTest(acceptanceTest)
}
// Class names
def classes = suites.values().collect { it.class }
// Creates the runners
def runners = classes.collect { new AcceptanceTestRunner(it, config) }
// Running the tests
boolean ok = runners
.collect { junit.run(it) }
.every { it.wasSuccessful() }
// XML output
xmlRunListener.render(new File('ontrack-acceptance.xml'))
What this runner does is to get all the test suite using the @AcceptanceTestSuite
annotation and to filter the tests out (using standard JUnit API) by checking the optional @AcceptanceTest
annotation against the current configuration.
When having the list of acceptance test classes, we pass them to the AcceptanceTestRunner:
// AcceptanceTestRunner.groovy
...
@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
def acceptanceTest = method.getAnnotation(AcceptanceTest)
if (acceptanceTest && !config.acceptTest(acceptanceTest)) {
notifier.fireTestIgnored(describeChild(method))
return
}
// Default
super.runChild(method, notifier)
}
This does the same thing, filtering using the optional @AcceptanceTest
annotation against the current configuration, but this time, it does it at test method level.
The config
object the acceptance test configuration and will be described below.
Configuration
Every parameter I want to use for my tests (connection parameters, file system, passwords, environment, etc.) can be defined using the Spring Boot configurable properties. For example:
@Component
@ConfigurationProperties(prefix = "ontrack")
class AcceptanceConfig {
private String url = 'http://localhost:8080'
private boolean disableSSL = false
private String admin = 'admin'
private Set<String> context = [] as Set
...
}
By injecting this object into my JUnit runner, I can use to filter my tests as described previously:
@Autowired
JUnitAcceptanceRunner(AcceptanceConfig config, ...) {
this.config = config
...
}
But since my tests are also Spring components, I can also inject this class into any test to have access to the environment:
...
@Autowired
private AcceptanceConfig config
...
String url = config.url
When running my acceptance tests, I can just pass parameters along:
--ontrack.url=http://production --ontrack.context=production
Conclusion
Using the power of Spring Boot, we can easily make our executable acceptance tests easily configurable.
- I use the
@AcceptanceTest(excludes="production")
annotation on my test classes and methods, and use the--context production
parameter when running the tests. - I can use the default Spring Boot configuration properties to define the environment my acceptance tests are running in.
You can see the complete code in ontrack.
This article is part of series of three about continuous delivery and automated acceptance tests using Docker and Spring Boot:
- Executable acceptance tests using Spring Boot
- Configurable acceptance tests using Spring Boot - this article
- Running acceptance tests against a Docker Spring Boot application - yet to come