Testing with Spring: An Introduction

63
Testing with Spring: An Introduction Sam Brannen @sam_brannen Spring eXchange | London, England | November 6, 2014 eXchange 2014

Transcript of Testing with Spring: An Introduction

Page 1: Testing with Spring: An Introduction

Testing with Spring: An Introduction Sam Brannen @sam_brannen

Spring eXchange | London, England | November 6, 2014

eXchange 2014

Page 2: Testing with Spring: An Introduction

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

Page 3: Testing with Spring: An Introduction

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

Page 4: Testing with Spring: An Introduction

4

A Show of Hands…

? ? ?

?

Page 5: Testing with Spring: An Introduction

5

Agenda

•  Unit testing

•  Integration testing

•  Context management and DI

•  Transactions and SQL scripts

•  Spring MVC and REST

•  Q&A

Page 6: Testing with Spring: An Introduction

6

Unit Testing

Page 7: Testing with Spring: An Introduction

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

Page 8: Testing with Spring: An Introduction

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

Page 9: Testing with Spring: An Introduction

9

EventService API

public interface EventService {

List<Event> findAll();

Event save(Event event);

void delete(Event event);}

Page 10: Testing with Spring: An Introduction

10

EventService Implementation (1/2)

@Service@Transactionalpublic class StandardEventService implements EventService {

private final EventRepository repository;

@Autowired public StandardEventService(EventRepository repository) { this.repository = repository; }

// ...

Page 11: Testing with Spring: An Introduction

11

EventService Implementation (2/2)

public Event save(final Event event) {

// additional business logic ...

return repository.save(event);}

Page 12: Testing with Spring: An Introduction

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); }

Page 13: Testing with Spring: An Introduction

13

EventService Unit Test (2/2)

@Testpublic 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)));}

Page 14: Testing with Spring: An Introduction

14

Integration Testing

Page 15: Testing with Spring: An Introduction

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

Page 16: Testing with Spring: An Introduction

16

Integration Test Complexity

Complexity

Dependency no external systems

external systems

container

Page 17: Testing with Spring: An Introduction

17

Modern Enterprise Java Apps

•  Integrate with external systems •  SMTP, FTP, LDAP, RDBMS, Web Services, JMS

•  Rely on container-provided functionality •  data sources, connection factories, transaction

managers

Page 18: Testing with Spring: An Introduction

18

Effective Integration Testing

•  Fast

•  Repeatable

•  Automated

•  Easy to configure

•  Run out-of-container

Page 19: Testing with Spring: An Introduction

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

Page 20: Testing with Spring: An Introduction

20

Spring TestContext Framework

Page 21: Testing with Spring: An Introduction

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.

Page 22: Testing with Spring: An Introduction

22

Testimony

“The Spring TestContext Framework is an excellent example of good annotation usage as it allows composition rather than inheritance.” - Costin Leau

Page 23: Testing with Spring: An Introduction

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

Page 24: Testing with Spring: An Introduction

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

Page 25: Testing with Spring: An Introduction

25

Spring 3.x Testing Themes (1/2)

•  Embedded databases –  <jdbc:embedded-database /> &

<jdbc:initialize-database /> –  EmbeddedDatabaseBuilder &

EmbeddedDatabaseFactoryBean

•  @Configuration classes

•  @ActiveProfiles

•  ApplicationContextInitializers

Page 26: Testing with Spring: An Introduction

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

Page 27: Testing with Spring: An Introduction

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)

Page 28: Testing with Spring: An Introduction

28

New in 4.1 – Context Config

•  Context config with Groovy scripts

•  Declarative configuration for test property sources

–  @TestPropertySource

Page 29: Testing with Spring: An Introduction

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

Page 30: Testing with Spring: An Introduction

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

Page 31: Testing with Spring: An Introduction

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

Page 32: Testing with Spring: An Introduction

32

Context Management

Page 33: Testing with Spring: An Introduction

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

Page 34: Testing with Spring: An Introduction

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

Page 35: Testing with Spring: An Introduction

35

@ContextHierarchy

•  Declared on test class –  Used with @ContextConfiguration

•  Configures hierarchies of test application contexts –  Useful for Spring MVC, Spring Batch, etc.

Page 36: Testing with Spring: An Introduction

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

Page 37: Testing with Spring: An Introduction

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

Page 38: Testing with Spring: An Introduction

38

Ex: Context Configuration

@RunWith(SpringJUnit4ClassRunner.class)@WebAppConfiguration@ContextHierarchy({

@ContextConfiguration(classes = RootConfig.class),@ContextConfiguration(classes = WebConfig.class)

})@ActiveProfiles("dev")public class ControllerIntegrationTests {

// ...

Page 39: Testing with Spring: An Introduction

39

Dependency Injection

Page 40: Testing with Spring: An Introduction

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

Page 41: Testing with Spring: An Introduction

41

EventService Integration Test

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class EventServiceIntegrationTests {

@Autowired EventService service;

@Test public void save() { Event event = new Event("test event"); Event savedEvent = service.save(event); assertNotNull(savedEvent.getId()); // ...}

Page 42: Testing with Spring: An Introduction

42

Transactional Tests

Page 43: Testing with Spring: An Introduction

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!

Page 44: Testing with Spring: An Introduction

44

Ex: Declarative Tx Management in Tests

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration@Transactionalpublic class TransactionalTests {

@Test public void withinTransaction() { /* ... */ } What if we want to

stop & start the transaction within the test method?

Page 45: Testing with Spring: An Introduction

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

Page 46: Testing with Spring: An Introduction

46

Ex: Programmatic Tx Mgmt in Tests @Testpublic 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...}

Page 47: Testing with Spring: An Introduction

47

SQL Script Execution

Page 48: Testing with Spring: An Introduction

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

Page 49: Testing with Spring: An Introduction

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();

Page 50: Testing with Spring: An Introduction

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>

Page 51: Testing with Spring: An Introduction

51

Ex: Populate Database in XML Config

<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:/schema_01.sql" /> <jdbc:script location="classpath:/schema_02.sql" /> <jdbc:script location="classpath:/data_01.sql" /> <jdbc:script location="classpath:/data_02.sql" /></jdbc:initialize-database>

Page 52: Testing with Spring: An Introduction

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() { /* ... */ }

Page 53: Testing with Spring: An Introduction

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

Page 54: Testing with Spring: An Introduction

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}

Page 55: Testing with Spring: An Introduction

55

Spring MVC Test Framework

Page 56: Testing with Spring: An Introduction

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

Page 57: Testing with Spring: An Introduction

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

Page 58: Testing with Spring: An Introduction

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; // ...

Page 59: Testing with Spring: An Introduction

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\"}")); }

Page 60: Testing with Spring: An Introduction

60

In Closing…

Page 61: Testing with Spring: An Introduction

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

Page 62: Testing with Spring: An Introduction

62

Blogs

•  Swiftmind Blog –  http://www.swiftmind.com/blog

•  Spring Blog –  http://spring.io/blog

Page 63: Testing with Spring: An Introduction

63

Q & A

Sam Brannen twitter: @sam_brannen www.slideshare.net/sbrannen www.swiftmind.com