testing web apps with spring framework

85

Upload: dmytro-chyzhykov

Post on 29-Nov-2014

449 views

Category:

Internet


1 download

DESCRIPTION

Testing Web Apps with Spring Framework

TRANSCRIPT

Page 1: Testing Web Apps with Spring Framework
Page 2: Testing Web Apps with Spring Framework

Testing Web Apps with Spring Framework

October 18, 2014 JavaDay’14, Kyiv

Dmytro Chyzhykov

Page 3: Testing Web Apps with Spring Framework

3

Slides and Code Examples

Page 4: Testing Web Apps with Spring Framework

- Build web applications with Spring Web MVC

- Familiar with mock objects unit testing (JUnit and Mockito)

4

Assumptions on Audience

Page 5: Testing Web Apps with Spring Framework

Spring Controllers Testing - Example Domain Model - Subject Under Test - Pure Unit Tests Spring MVC Test Framework - Standalone Server-Side Integration Tests - Web Application Context Server-Side Integration Tests

Further materials

Q&A

5

Agenda

Page 6: Testing Web Apps with Spring Framework

Example Domain Model

6

Page 7: Testing Web Apps with Spring Framework

7

Yandex.TV Service

Page 8: Testing Web Apps with Spring Framework

8

Domainpublic class Channel { private Integer id; private String title; ! // constructors, getters/setters }

Page 9: Testing Web Apps with Spring Framework

9

Domainpublic class Channel { private Integer id; private String title; ! // constructors, getters/setters }

DAO@Repository @Transactional public interface ChannelRepository { ! Channel findOne(Integer id); !}

Page 10: Testing Web Apps with Spring Framework

Subject Under Test

10

Page 11: Testing Web Apps with Spring Framework

11

Subject Under Test@Controller @RequestMapping("/channels") public class ChannelController { }

Page 12: Testing Web Apps with Spring Framework

12

Subject Under Test@Controller @RequestMapping("/channels") public class ChannelController { @Autowired ChannelRepository channelRepository; }

Page 13: Testing Web Apps with Spring Framework

13

Subject Under Test@Controller @RequestMapping("/channels") public class ChannelController { @Autowired ChannelRepository channelRepository; ! @ResponseBody @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Channel getChannel(@PathVariable int id) { // ... } }

Page 14: Testing Web Apps with Spring Framework

14

Subject Under Test@Controller @RequestMapping("/channels") public class ChannelController { @Autowired ChannelRepository channelRepository; ! @ResponseBody @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Channel getChannel(@PathVariable int id) { Channel channel = channelRepository.findOne(id); ! if (channel != null) { return channel; } ! // ... } }

Page 15: Testing Web Apps with Spring Framework

15

Subject Under Test@Controller @RequestMapping("/channels") public class ChannelController { @Autowired ChannelRepository channelRepository; ! @ResponseBody @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Channel getChannel(@PathVariable int id) { Channel channel = channelRepository.findOne(id); ! if (channel != null) { return channel; } ! throw new ChannelNotFoundException(); } }

Page 16: Testing Web Apps with Spring Framework

16

Exception@ResponseStatus(HttpStatus.NOT_FOUND) public class ChannelNotFoundException extends RuntimeException { ! // constructors !}

Page 17: Testing Web Apps with Spring Framework

ChannelController behaviour

- Positive test case When we are looking for an existent channel by its id

- Negative test case When we are looking for an absent channel by its id

17

What we are going to test

Page 18: Testing Web Apps with Spring Framework

Pure Unit Testing

18

Page 19: Testing Web Apps with Spring Framework

19http://www.tubelinescaffolding.co.uk/industrial-scaffolding.htm

Scaffolding

Page 20: Testing Web Apps with Spring Framework

20

Unit Test Scaffolding!public class ChannelControllerTest { }

Page 21: Testing Web Apps with Spring Framework

21

Unit Test Scaffolding!public class ChannelControllerTest { @Mock private ChannelRepository channelRepository; }

Page 22: Testing Web Apps with Spring Framework

22

Unit Test Scaffolding!public class ChannelControllerTest { @Mock private ChannelRepository channelRepository; ! @InjectMocks private ChannelController channelController = // optional new ChannelController(); }

Page 23: Testing Web Apps with Spring Framework

23

Unit Test Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerTest { @Mock private ChannelRepository channelRepository; ! @InjectMocks private ChannelController channelController = // optional new ChannelController(); }

Page 24: Testing Web Apps with Spring Framework

24

Unit Test Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerTest { @Mock private ChannelRepository channelRepository; ! @InjectMocks private ChannelController channelController = // optional new ChannelController(); !! @Mock private Channel channel; // dummy // test cases go here }

Page 25: Testing Web Apps with Spring Framework

25

Positive Test Case@Test public void itShouldFindChannel() { when(channelRepository.findOne(1)) .thenReturn(channel); }

Page 26: Testing Web Apps with Spring Framework

26

Positive Test Case@Test public void itShouldFindChannel() { when(channelRepository.findOne(1)) .thenReturn(channel); ! assertThat( channelController.getChannel(1), is(channel) ); }

Page 27: Testing Web Apps with Spring Framework

27

Negative Test Case@Test public void itShouldNotFoundChannel() { // optional when(channelRepository.findOne(-1)) .thenReturn(null); }

Page 28: Testing Web Apps with Spring Framework

28

Negative Test Case@Test(expected = ChannelNotFoundException.class) public void itShouldNotFoundChannel() { // optional when(channelRepository.findOne(-1)) .thenReturn(null); ! channelController.getChannel(-1); }

Page 29: Testing Web Apps with Spring Framework

- Easy to write - Incredibly fast (a few milliseconds per test case)

29

Pros

Page 30: Testing Web Apps with Spring Framework

- Can use Spring mocks from org.springframework.mock.web - MockHttpServletRequest/Response/Session - MockMultipartFile - MockFilterChain … - ModelAndViewAssert from org.springframework.test.web to apply asserts on a resulting ModelAndView

30

Additional Capabilities on Demand

Page 31: Testing Web Apps with Spring Framework

- A lot left untested - Request mappings - Type conversion - Transactions - Data binding - Validation - Filters - … - No Spring annotations used- No DispatcherServlet interactions- No actual Spring MVC configuration loaded

31

Caveats

Page 32: Testing Web Apps with Spring Framework

32http://futurama.wikia.com/wiki/File:GoodNewsEveryone.jpg

Good news everyone!

Page 33: Testing Web Apps with Spring Framework

Spring MVC Test Framework since 3.2

33

Page 34: Testing Web Apps with Spring Framework

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.1.1.RELEASE</version></dependency>

34

Dependency

Page 35: Testing Web Apps with Spring Framework

35

Server-Side Integration Testing without a Running Servlet Container

Web Application

ContextDispatcherServlet

Tests

Controllers

MockMvc

Page 36: Testing Web Apps with Spring Framework

- Response status, headers, content- Spring MVC and Servlet specific results - Model, flash, session, request attributes - Mapped controller method - Resolved exceptions- Various options for asserting the response body - JsonPath, XPath, XMLUnit

36

What can be tested

Page 37: Testing Web Apps with Spring Framework

- Almost all template technologies are supported - JSON, XML, Velocity, Freemarker, Thymeleaf, PDF etc. - Except JSP (because it relies on Servlet Container) - you can assert only on the selected JSP view name- No actual redirecting or forwarding - you can assert the redirected or forwarded URL

37

Testing View Layer

Page 38: Testing Web Apps with Spring Framework

Standalone setup for testing one individual controller at a time without actual Spring MVC configuration loading

38

MockMvc “Standalone” Setup

private ChannelController controller = //... !private MockMvc mockMvc; !public void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(controller) .build(); }

Page 39: Testing Web Apps with Spring Framework

39

MockMvc “Standalone” SetupmockMvc = MockMvcBuilders.standaloneSetup(controller) .setValidator(...) .setViewResolvers(...) .setHandlerExceptionResolvers(...) .setMessageConverters(...) .setLocaleResolver(...) .addFilter(...) //... .build();

Page 40: Testing Web Apps with Spring Framework

With actual Spring MVC configuration loading

40

MockMvc Web App Context Setup

// Scaffolding is omitted !@Autowired private WebApplicationContext wac; !@Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(wac) .build(); }

Page 41: Testing Web Apps with Spring Framework

41

Creating and Performing RequestsMockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/channels/1") .param("foo", "bar") .header(...) .cookie(...) .locale(...) .characterEncoding("UTF-8") .accept("application/json") .flashAttr("flash-key", "value") // ... .sessionAttr("key", “value"); !!mockMvc.perform(request);

Page 42: Testing Web Apps with Spring Framework

42

Applying AssertsmockMvc.perform(request) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.id").value(1)) // ... .andExpect(xpath("...")...) .andExpect(header()...) .andExpect(cookies()...) .andExpect(model()...) .andExpect(view()...) .andExpect(content()...) .andExpect(flash()...) .andExpect(redirectedUrl("..."));

Page 43: Testing Web Apps with Spring Framework

43

Resolved Exception AssertMvcResult mvcResult = mockMvc .perform(...) // ... .andReturn(); ! assertThat( mvcResult.getResolvedException(), instanceOf(ChannelNotFoundException.class) );

Page 44: Testing Web Apps with Spring Framework

- MockMvcBuilders.* to set up MockMvc instances- MockMvcRequestBuilders.* to create requests- MockMvcResultMatchers.* for request result assertions on

44

Useful Static Imports

Page 45: Testing Web Apps with Spring Framework

Standalone Server-Side Integration Tests

Page 46: Testing Web Apps with Spring Framework

46

Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerStandaloneIT { @Mock private ChannelRepository channelRepository; @InjectMocks private ChannelController channelController = new ChannelController(); }

Page 47: Testing Web Apps with Spring Framework

47

Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerStandaloneIT { @Mock private ChannelRepository channelRepository; @InjectMocks private ChannelController channelController = new ChannelController(); private Channel channel = new Channel(1, "MTV"); }

Page 48: Testing Web Apps with Spring Framework

48

Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerStandaloneIT { @Mock private ChannelRepository channelRepository; @InjectMocks private ChannelController channelController = new ChannelController(); private Channel channel = new Channel(1, "MTV"); ! private MockMvc mockMvc; }

Page 49: Testing Web Apps with Spring Framework

49

Scaffolding@RunWith(MockitoJUnitRunner.class) public class ChannelControllerStandaloneIT { @Mock private ChannelRepository channelRepository; @InjectMocks private ChannelController channelController = new ChannelController(); private Channel channel = new Channel(1, "MTV"); ! private MockMvc mockMvc; ! @Before public void setUp() { mockMvc = standaloneSetup(channelController) .build(); } // test cases go here }

Page 50: Testing Web Apps with Spring Framework

50

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); }

Page 51: Testing Web Apps with Spring Framework

51

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); ! mockMvc.perform(get("/channels/1") .accept("application/json")) }

Page 52: Testing Web Apps with Spring Framework

52

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); ! mockMvc.perform(get("/channels/1") .accept("application/json")) .andExpect(status().isOk()) }

Page 53: Testing Web Apps with Spring Framework

53

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); ! mockMvc.perform(get("/channels/1") .accept("application/json")) .andExpect(status().isOk()) .andExpect(content() .contentType("application/json;charset=UTF-8")) }

Page 54: Testing Web Apps with Spring Framework

54

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); ! mockMvc.perform(get("/channels/1") .accept("application/json")) .andExpect(status().isOk()) .andExpect(content() .contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$.id").value(1)) }

Page 55: Testing Web Apps with Spring Framework

55

Positive Test Case@Test public void itShouldFindChannel() throws Exception { when(channelRepository.findOne(1)) .thenReturn(channel); ! mockMvc.perform(get("/channels/1") .accept("application/json")) .andExpect(status().isOk()) .andExpect(content() .contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.title").value("MTV")); }

Page 56: Testing Web Apps with Spring Framework

56

Negative Test Case@Test public void itShouldNotFindChannel() throws Exception { // optional when(channelRepository.findOne(-1)).willReturn(null); }

Page 57: Testing Web Apps with Spring Framework

57

Negative Test Case@Test public void itShouldNotFindChannel() throws Exception { // optional when(channelRepository.findOne(-1)).willReturn(null); ! mockMvc.perform(get("/channels/-1") .accept("application/json")) }

Page 58: Testing Web Apps with Spring Framework

58

Negative Test Case@Test public void itShouldNotFindChannel() throws Exception { // optional when(channelRepository.findOne(-1)).willReturn(null); ! mockMvc.perform(get("/channels/-1") .accept("application/json")) .andExpect(status().isNotFound()); }

Page 59: Testing Web Apps with Spring Framework

59

Negative Test Case@Test public void itShouldNotFindChannel() throws Exception { // optional when(channelRepository.findOne(-1)).willReturn(null); ! MvcResult mvcResult = mockMvc .perform(get("/channels/-1") .accept("application/json")) .andExpect(status().isNotFound()) .andReturn(); ! assertThat(mvcResult.getResolvedException(), instanceOf(ChannelNotFoundException.class)); }

Page 60: Testing Web Apps with Spring Framework

60

Demo

Page 61: Testing Web Apps with Spring Framework

ChannelController instantiated

Mock of ChannelRepository injected

MockMvc was set-upped

MockHttpServletRequest prepared

Executed via DispatcherServlet

Assertions applied on the resulting MockHttpServletResponse

Assertions applied on the resulting MvcResult

61

What happened

Page 62: Testing Web Apps with Spring Framework

- Easy to write - Uses Spring annotations- Always interacts with DispatcherServlet

62

Pros

Page 63: Testing Web Apps with Spring Framework

- A bit slow (about 1 second for the first test case)- No Actual Spring MVC configuration loaded

63

Caveats

Page 64: Testing Web Apps with Spring Framework

Web Application Context Server-Side Integration Tests

Page 65: Testing Web Apps with Spring Framework

65

Scaffolding!!!!!!!!public class ChannelControllerWebAppIT { }

Page 66: Testing Web Apps with Spring Framework

66

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) !!!!!!!public class ChannelControllerWebAppIT { }

Page 67: Testing Web Apps with Spring Framework

67

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration !!!!!!public class ChannelControllerWebAppIT { @Autowired private WebApplicationContext wac; }

Page 68: Testing Web Apps with Spring Framework

68

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "file:somewhere/servlet-context.xml", "file:somewhere/persistence-context.xml" }) !!public class ChannelControllerWebAppIT { @Autowired private WebApplicationContext wac; }

Page 69: Testing Web Apps with Spring Framework

69

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "file:somewhere/servlet-context.xml", "file:somewhere/persistence-context.xml" }) @Transactional !public class ChannelControllerWebAppIT { @Autowired private WebApplicationContext wac; }

Page 70: Testing Web Apps with Spring Framework

70

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "file:somewhere/servlet-context.xml", "file:somewhere/persistence-context.xml" }) @Transactional @Sql(scripts = "classpath:test-channel-seeds.sql") public class ChannelControllerWebAppIT { @Autowired private WebApplicationContext wac; }

Page 71: Testing Web Apps with Spring Framework

71

Scaffolding@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "file:somewhere/servlet-context.xml", "file:somewhere/persistence-context.xml" }) @Transactional @Sql(scripts = "classpath:test-channel-seeds.sql") public class ChannelControllerWebAppIT { @Autowired private WebApplicationContext wac; ! private MockMvc mockMvc; ! @Before public void setUp() { mockMvc = webAppContextSetup(wac).build(); } }

Page 72: Testing Web Apps with Spring Framework

72

Positive Test Case@Test public void itShouldFindChannel() throws Exception { mockMvc.perform(get("/channels/1") .accept("application/json")) .andExpect(status().isOk()) .andExpect(content() .contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.title").value("MTV")); }

Page 73: Testing Web Apps with Spring Framework

73

Negative Test Case@Test public void itShouldNotFindChannel() throws Exception { MvcResult mvcResult = mockMvc .perform(get("/channels/-1") .accept("application/json")) .andExpect(status().isNotFound()) .andReturn(); ! assertThat(mvcResult.getResolvedException(), instanceOf(ChannelNotFoundException.class)); }

Page 74: Testing Web Apps with Spring Framework

74

Demo

Page 75: Testing Web Apps with Spring Framework

Actual Web MVC application context loaded

MockHttpServletRequest prepared

Executed via DispatcherServlet

Assertions applied on the resulting MockHttpServletResponse

Assertions applied on the resulting MvcResult

75

What happened

Page 76: Testing Web Apps with Spring Framework

- Easy to write - Loads actual Spring MVC configuration (cacheable)- Uses Spring annotations- Always Interacts with DispatcherServlet

76

Pros

Page 77: Testing Web Apps with Spring Framework

- Slower than the “Standalone” option (depends on amount of beans in a particular Spring Mvc configuration)- Does not replace end-to-end testing like Selenium

77

Caveats

Page 78: Testing Web Apps with Spring Framework

Further Materials

Page 79: Testing Web Apps with Spring Framework

Integration between Spring MVC Test Framework and HtmlUnit.

Repository: https://github.com/spring-projects/spring-test-htmlunitDocumentation: https://github.com/spring-projects/spring-test-htmlunit/blob/master/src/asciidoc/index.adoc

79

Spring MVC Test HtmlUnit

Page 80: Testing Web Apps with Spring Framework

Spring Framework Reference DocumentationChapter 11.3 Integration Testinghttp://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#integration-testing

spring-test artifact source code https://github.com/spring-projects/spring-framework/tree/master/spring-test

Spring MVC Showcase https://github.com/spring-projects/spring-mvc-showcase

Code exampleshttps://github.com/ffbit/spring-mvc-test-framework-examples

80

Links

Page 81: Testing Web Apps with Spring Framework

Webinar: Testing Web Applications with Spring 3.2 by Sam Brannen (Swiftmind) and Rossen Stoyanchevhttps://www.youtube.com/watch?v=K6x8LE7Qd1Q

Spring Testingby Mattias Seversonhttps://www.youtube.com/watch?v=LYVJ69h76nw

!

81

Videos

Page 82: Testing Web Apps with Spring Framework

Thank you! !

Questions?

Page 83: Testing Web Apps with Spring Framework

[email protected]

Dmytro Chyzhykov

[email protected]

ffbit

Senior Software Engineer at Yandex Media ServicesKyiv, Ukraine

@dcheJava

Page 84: Testing Web Apps with Spring Framework

84

Slides and Code Examples

Page 85: Testing Web Apps with Spring Framework

85http://www.dotatalk.com/wp-content/uploads/2013/09/All-hail-King-Hypno-Toad.jpg