The Spring Framework has always embraced testing as a first class citizen. Spring-based components should be modular, easy to wire together via dependency injection, and therefore easy to test. In fact, when well designed following a POJO programming model, a component in a Spring application can be unit tested without using Spring at all. And when you take the step toward developing integration tests, Spring's testing support is there to make your job easy.
Join Spring Test component lead Sam Brannen in this talk to learn about the basics for Spring's unit and integration testing support. This talk will provide attendees an overview of the following topics: unit testing without Spring, integration testing with Spring, loading application contexts (with and without context hierarchies), injecting dependencies into tests, transaction management for tests, SQL script execution, testing Spring MVC and REST web applications, and more.
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Testing with Spring: An Introduction
1. Testing with Spring:
An Introduction
Sam Brannen
@sam_brannen
Spring eXchange | London, England | November 6, 2014
eXchange 2014
2. 2
Sam Brannen
• Spring and Java Consultant @ Swiftmind
• Java Developer for over 15 years
• Spring Framework Core Committer since 2007
– Component lead for spring-test
• Spring Trainer
• Speaker on Spring, Java, and testing
• Swiss Spring User Group Lead
3. 3
Swiftmind
Experts in Spring and Enterprise Java
Areas of expertise
• Spring *
• Java EE
• Software Architecture
• Software Engineering Best Practices
Where you find us
• Zurich, Switzerland
• @swiftmind
• http://www.swiftmind.com
7. 7
Unit Tests
• are simple to set up
• use dynamic mocks or stubs for dependencies
• instantiate the SUT, execute the code, and assert
expectations
• run fast
• but only test a single unit
8. 8
Spring and Unit Testing
• POJO-based programming model
– Program to interfaces
– IoC / Dependency Injection
– Out-of-container testability
– Third-party mocking frameworks (Mockito, …)
• Spring testing mocks/stubs
– Servlet
– Portlet
– JNDI
– Spring Environment
12. 12
EventService Unit Test (1/2)
public class StandardEventServiceTests {
private StandardEventService service;
private EventRepository repository;
@Before
public void setUp() {
repository = mock(EventRepository.class);
service = new StandardEventService(repository);
}
13. 13
EventService Unit Test (2/2)
@Test
public void save() {
Event event = new Event();
event.setName("test event");
event.setDescription("testing");
given(repository.save(any(Event.class)))
.willReturn(event);
Event savedEvent = service.save(event);
assertThat(savedEvent, is(equalTo(event)));
}
15. 15
Integration Tests
• test interactions between multiple components
• relatively easy to set up…
• without external system dependencies
• challenging to set up…
• with external system dependencies
• more challenging…
• if application code depends on the container
19. 19
Out-of-container
• Zero reliance on availability of external systems
• Can be run anywhere
• developer workstation
• CI server
• Approximate the production environment
• Embedded database
• In-memory SMTP server, FTP server, JMS broker
21. 21
In a nutshell…
The Spring TestContext Framework
… provides annotation-driven unit and integration testing
support that is agnostic of the testing framework in use
… with a strong focus on convention over configuration
and reasonable defaults that can be overridden through
annotation-based configuration
… and integrates with JUnit and TestNG out of the box.
22. 22
Testimony
“The Spring TestContext Framework is an
excellent example of good annotation usage
as it allows composition rather than
inheritance.”
- Costin Leau
23. 23
Feature Set
• Context management and caching
• Dependency Injection of test fixtures
• Transaction management
• SQL script execution
• Spring MVC and REST
• Extension points for customization
24. 24
Spring 2.5 Testing Themes
• @ContextConfiguration
– XML config files
– Context caching
• @TestExecutionListeners
– Dependency injection: @Autowired, etc.
– @DirtiesContext
– @Transactional, @BeforeTransaction, etc.
• SpringJUnit4ClassRunner
• Abstract base classes for JUnit and TestNG
26. 26
Spring 3.x Testing Themes (2/2)
• @WebAppConfiguration
– Loading WebApplicationContexts
– Testing request- and session-scoped beans
• @ContextHierarchy
– Web, Batch, etc.
• Spring MVC Test framework
– Server-side MVC and REST tests
– Client-side REST tests
27. 27
Spring 4.0 Testing Themes
• SocketUtils
– Scan for available UDP & TCP ports
• ActiveProfilesResolver API
– Programmatic alternative to static profile strings
– Set via resolver attribute in @ActiveProfiles
• Meta-annotation support for tests
– Attribute overrides (optional and required)
28. 28
New in 4.1 – Context Config
• Context config with Groovy scripts
• Declarative configuration for test property sources
– @TestPropertySource
29. 29
New in 4.1 – Transactions & SQL
• Programmatic test transaction management
– TestTransaction API
• Declarative SQL script execution
– @Sql, @SqlConfig, @SqlGroup
• Improved docs for transactional tests
30. 30
New in 4.1 – TestExecutionListeners
• Automatic discovery of default TestExecutionListeners
– Uses SpringFactoriesLoader
– Already used by Spring Security
• Merging custom TestExecutionListeners with defaults
– @TestExecutionListeners(mergeMode=
MERGE_WITH_DEFAULTS)
– Defaults to REPLACE_DEFAULTS
31. 31
New in 4.1 – Spring MVC Test
• Assert JSON responses with JSON Assert
– Complements JSONPath support
• Create MockMvcBuilder recipes with MockMvcConfigurer
– Developed to apply Spring Security setup but can be used
by anyone
• AsyncRestTemplate support in MockRestServiceServer
– For asynchronous client-side testing
33. 33
@ContextConfiguration
• Declared on test class
– Inheritance supported but overridable
– Spring Boot: use @SpringApplicationConfiguration
• ContextLoader loads ApplicationContext based on:
– Configuration in annotation
– Or by detecting default configuration
• Supports:
– XML configuration files
– @Configuration classes
– Groovy scripts
34. 34
@WebAppConfiguration
• Declared on test class
• Instructs Spring to load a WebApplicationContext for the test
• ServletTestExecutionListener ensures that Servlet API mocks
are properly configured
• Most often used with the Spring MVC Test Framework
35. 35
@ContextHierarchy
• Declared on test class
– Used with @ContextConfiguration
• Configures hierarchies of test application contexts
– Useful for Spring MVC, Spring Batch, etc.
36. 36
@ActiveProfiles
• Declared on test class
– Used with @ContextConfiguration
• Configures which bean definition profiles should be active
when the test’s ApplicationContext is loaded
• Active profiles can be configured:
– Declaratively within the annotation
– Or programmatically via a custom ActiveProfilesResolver
37. 37
Context Caching
• The Spring TestContext Framework caches all application
contexts within the same JVM process!
• Cache key is generated based on configuration in:
– @ContextConfiguration
– @ContextHierarchy
– @WebAppConfiguration
– @ActiveProfiles
• Use @DirtiesContext to remove a given test from the cache
40. 40
DI within Integration Tests
• Dependencies can be injected into a test instance from
the test’s ApplicationContext
• Using @Autowired, @Inject, @PersistenceContext, etc.
• The ApplicationContext itself can also be injected into test
instances…
• @Autowired ApplicationContext
• @Autowired WebApplicationContext
43. 43
Transactions in Spring
• Spring-managed transactions: managed by Spring in
the ApplicationContext
– @Transactional and AOP
• Application-managed transactions: managed
programmatically within application code
– TransactionTemplate and
TransactionSynchronizationManager
• Test-managed transactions: managed by the Spring
TestContext Framework
– @Transactional on test classes and test methods
– Transaction is rolled back by default!
44. 44
Ex: Declarative Tx Management in Tests
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class TransactionalTests {
@Test
public void withinTransaction() {
/* ... */
} What if we want to
stop & start the
transaction within
the test method?
45. 45
TestTransaction API
• Static methods for interacting with test-managed
transactions
– isActive()
– isFlaggedForRollback()
– flagForCommit()
– flagForRollback()
– end()
– start()
query status
change default rollback setting
end: roll back or commit based on flag
start: new tx with default rollback setting
46. 46
Ex: Programmatic Tx Mgmt in Tests
@Test
public void withinTransaction() {
// assert initial state in test database:
assertNumUsers(2);
deleteFromTables("user");
// changes to the database will be committed
TestTransaction.flagForCommit();
TestTransaction.end();
assertNumUsers(0);
TestTransaction.start();
// perform other actions against the database that will
// be automatically rolled back after test completes...
}
48. 48
SQL Script Execution Options
• At ApplicationContext startup via:
• <jdbc> XML namespace
• EmbeddedDatabaseBuilder in Java Config
• Programmatically during tests with:
• ScriptUtils, ResourceDatabasePopulator, or abstract
transactional base test classes for JUnit and TestNG
• Declaratively via @Sql, @SqlConfig, & @SqlGroup
• Per test method
• Per test class
49. 49
Ex: Embedded Database in Java Config
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
// ...
db.shutdown();
50. 50
Ex: Embedded Database in XML Config
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:/schema.sql" />
<jdbc:script location="classpath:/user_data.sql" />
</jdbc:embedded-database>
52. 52
Ex: @Sql in Action
@ContextConfiguration
@Sql({ "schema1.sql", "data1.sql" })
public class SqlScriptsTests {
@Test
public void classLevelScripts() { /* ... */ }
@Test
@Sql({ "schema2.sql", "data2.sql" })
public void methodLevelScripts() { /* ... */ }
53. 53
@Sql - Repeatable Annotation (Java 8)
@Test
@Sql(
scripts="/test-schema.sql",
config = @SqlConfig(commentPrefix = "`")
@Sql("/user-data.sql")
public void userTest() {
// code that uses the test schema and test data
}
Schema uses
custom syntax
54. 54
@Sql wrapped in @SqlGroup (Java 6/7)
@Test
@SqlGroup({
@Sql(
scripts="/test-schema.sql",
config = @SqlConfig(commentPrefix = "`"),
@Sql("/user-data.sql")
})
public void userTest() {
// code that uses the test schema and test data
}
56. 56
What is Spring MVC Test?
• Dedicated support for testing Spring MVC applications
• Fluent API
• Very easy to write
• Includes client and server-side support
• Servlet container not required
57. 57
Details
• Included in spring-test module of Spring Framework
3.2
• Builds on
– TestContext framework for loading Spring MVC
configuration
– MockHttpServlet[Request|Response] and other
mock types
• Server-side tests involve DispatcherServlet
• Client-side REST testing for code using RestTemplate
58. 58
Ex: Web Integration Test (1/2)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = RootConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
@ActiveProfiles("dev")
public class ControllerIntegrationTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
// ...
59. 59
Ex: Web Integration Test (2/2)
@Before
public void setup() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.wac).build();
}
@Test
public void person() throws Exception {
this.mockMvc.perform(get("/person/42")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string("{"name":"Sam"}"));
}
61. 61
Spring Resources
• Spring Framework
– http://projects.spring.io/spring-framework
• Spring Guides
– http://spring.io/guides
• Spring Forums
– http://forum.spring.io
• Spring JIRA
– https://jira.spring.io
• Spring on GitHub
– https://github.com/spring-projects/spring-framework