Spring 3.1 and MVC Testing Support
Upcoming SlideShare
Loading in...5
×
 

Spring 3.1 and MVC Testing Support

on

  • 10,916 vues

This session will give attendees an overview of the new testing features in Spring 3.1 as well the new Spring MVC test support. Sam Brannen will demonstrate how to use the Spring TestContext Framework ...

This session will give attendees an overview of the new testing features in Spring 3.1 as well the new Spring MVC test support. Sam Brannen will demonstrate how to use the Spring TestContext Framework to write integration tests for Java-based Spring configuration using @Configuration classes. He'll then compare and contrast this approach with XML-based configuration and follow up with a discussion of the new testing support for bean definition profiles. Next, Rossen Stoyanchev will show attendees how testing server-side code with annotated controllers and client-side code with the RestTemplate just got a whole lot easier with the new Spring MVC test support. Come to this session to see these new Spring testing features in action and learn how you can get involved in the Spring MVC Test Support project.

Statistiques

Vues

Total des vues
10,916
Vues sur SlideShare
10,404
Vues externes
512

Actions

J'aime
8
Téléchargements
217
Commentaires
0

8 Ajouts 512

http://www.swiftmind.com 414
http://www.springone2gx.com 65
http://local-swiftmind.com 16
http://www.nofluffjuststuff.com 6
http://springone2gx.com 5
http://www.slashdocs.com 4
http://a0.twimg.com 1
http://www.docshut.com 1
Plus...

Accessibilité

Catégories

Détails de l'import

Uploaded via as Adobe PDF

Droits d'utilisation

© Tous droits réservés

Report content

Signalé comme inapproprié Signaler comme inapproprié
Signaler comme inapproprié

Indiquez la raison pour laquelle vous avez signalé cette présentation comme n'étant pas appropriée.

Annuler
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Votre message apparaîtra ici
    Processing...
Poster un commentaire
Modifier votre commentaire

Spring 3.1 and MVC Testing Support Spring 3.1 and MVC Testing Support Presentation Transcript

  • Spring 3.1 and MVC Testing Support Sam Brannen Swiftmind Rossen Stoyanchev SpringSource, VMwareSpringOne 2GXOctober 28, 2011
  • Sam BrannenSenior Software Consultant @ SwiftmindJava developer with 13+ years experienceSpring Framework Core DeveloperLead author of Spring in a NutshellPrevious SpringSource dm Server™ developerPresenter on Spring, Java, OSGi, and testing
  • Rossen StoyanchevStaff Engineer SpringSource, VMwareSpring MVC, Spring Web Flow committerTeach and consult, Spring ProjectsSpring Web course authorNYC area View slide
  • Goals of the PresentationGain an overview of testing featuresin Spring 3.1Learn about the new Spring MVCTest Support project View slide
  • AgendaSpring TestContext FrameworkTesting with @Configuration ClassesTesting with Environment ProfilesSpring MVC Test SupportQ&A
  • Show of Hands...JUnit / TestNGSpring 2.5 / 3.0 / 3.1Integration testing with SpringSpring TestContext FrameworkSpring MVCSpring MVC Test Support
  • Spring TestContext Framework
  • Introduced in Spring 2.5
  • Revised in Spring 3.1
  • Unit and integration testing
  • Annotation-driven
  • Convention over Configuration
  • JUnit & TestNG
  • Spring & Unit TestingPOJO-based programming modelProgram to interfacesIoC / Dependency InjectionOut-of-container testabilityTesting mocks/stubs for various APIs: Servlet,Portlet, JNDIGeneral purpose testing utilities ReflectionTestUtils ModelAndViewAssert
  • Spring & Integration Testing ApplicationContext management & caching Dependency injection of test instances Transactional test management with default rollback semantics SimpleJdbcTestUtils JUnit 3.8 support classes are deprecated as of Spring 3.0/3.1
  • Spring Test AnnotationsApplication Contexts @ContextConfiguration, @DirtiesContext@TestExecutionListenersDependency Injection @Autowired, @Qualifier, @Inject, …Transactions @Transactional, @TransactionConfiguration, @Rollback, @BeforeTransaction, @AfterTransaction
  • Spring JUnit Annotations Testing Profiles groups, not bean definition profiles @IfProfileValue, @ProfileValueSourceConfiguration JUnit extensions @Timed, @Repeat
  • Using the TestContext FrameworkUse the SpringJUnit4ClassRunner forJUnit 4.5+Instrument test class withTestContextManager for TestNGExtend one of the base classes Abstract(Transactional)[JUnit4|TestNG]Spri
  • Example: @POJO Test Classpublic class OrderServiceTests { @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)public class OrderServiceTests { @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class OrderServiceTests { @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration // defaults to// OrderServiceTests-context.xml in same packagepublic class OrderServiceTests { @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration // defaults to// OrderServiceTests-context.xml in same packagepublic class OrderServiceTests { @Autowired private OrderService orderService; @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration // defaults to// OrderServiceTests-context.xml in same package@Transactionalpublic class OrderServiceTests { @Autowired private OrderService orderService; @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration // defaults to// OrderServiceTests-context.xml in same package@Transactionalpublic class OrderServiceTests { @Autowired private OrderService orderService; @BeforeTransaction public void verifyInitialDatabaseState() { … } @Test public void testOrderService() { … }}
  • Example: @POJO Test Class@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration // defaults to// OrderServiceTests-context.xml in same package@Transactionalpublic class OrderServiceTests { @Autowired private OrderService orderService; @BeforeTransaction public void verifyInitialDatabaseState() { … } @Before public void setUpTestDataWithinTransaction() { … } @Test public void testOrderService() { … }}
  • Core Components
  • TestContextTracks context for current testDelegates to a ContextLoaderCaches ApplicationContext
  • TestContextManagerManages the TestContextSignals events to listeners: before: before-class methods after: test instantiation before: before methods after: after methods after: after-class methods
  • TestExecutionListener SPIReacts to test execution events Receives reference to current TestContextOut of the box: DependencyInjectionTestExecutionListener DirtiesContextTestExecutionListener TransactionalTestExecutionListener
  • TestExecutionListener
  • TEL: Prepare Instance
  • TEL: Befores and Afters
  • ContextLoader SPIStrategy for loading application contexts from resource locationsOut of the box: GenericXmlContextLoader GenericPropertiesContextLoader
  • ContextLoader 2.5
  • Putting it all together
  • New in Spring 3.1
  • Support for ...
  • testing with @Configuration classes
  • and
  • testing with environment profiles
  • made possible by ...
  • SmartContextLoader
  • AnnotationConfigContextLoader
  • DelegatingSmartContextLoader
  • updated context cache key generation
  • SmartContextLoader SPISupersedes ContextLoaderStrategy for loading applicationcontextsFrom @Configuration classes orresource locationsSupports environment profiles
  • ImplementationsGenericXmlContextLoaderGenericPropertiesContextLoaderAnnotationConfigContextLoaderDelegatingSmartContextLoader
  • ContextLoader 2.5
  • ContextLoader 3.1
  • Testing with@Configuration Classes
  • @ContextConfiguration accepts a new classes attribute...
  • @ContextConfiguration( classes={DataAccessConfig.class, ServiceConfig.class})
  • First, a review with XML config for comparison...
  • OrderServiceTest-context.xml<?xml version="1.0" encoding="UTF-8"?><beans ...> <!-- will be injected into OrderServiceTest --> <bean id="orderService" class="com.example.OrderServiceImpl"> <!-- set properties, etc. --> </bean> <!-- other beans --></beans>
  • OrderServiceTest.javapackage com.example;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testOrderService() { // test the orderService }}
  • Lets rework that example to use a @Configuration class and the new AnnotationConfigContextLoader...
  • OrderServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)public class OrderServiceTest { @Autowired private OrderService orderService; // @Test methods ...}
  • OrderServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration( loader=AnnotationConfigContextLoader.class)public class OrderServiceTest { @Autowired private OrderService orderService; // @Test methods ...}
  • OrderServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class OrderServiceTest { @Autowired private OrderService orderService; // @Test methods ...}
  • OrderServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class OrderServiceTest { @Configuration static class Config { } @Autowired private OrderService orderService; // @Test methods ...}
  • OrderServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class OrderServiceTest { @Configuration static class Config { @Bean // will be injected into OrderServiceTest public OrderService orderService() { OrderService orderService = new OrderServiceImpl(); // set properties, etc. return orderService; } } @Autowired private OrderService orderService; // @Test methods ...}
  • Whats changed?No XMLBean definitions are converted toJava using @Configuration and @BeanOtherwise, the test remainsunchanged
  • But what if we dont want a static inner @Configuration class?
  • Just externalize the config...
  • OrderServiceConfig.java@Configurationpublic class OrderServiceConfig { @Bean // will be injected into OrderServiceTest public OrderService orderService() { OrderService orderService = new OrderServiceImpl(); // set properties, etc. return orderService; }}
  • And reference the config classes in @ContextConfiguration...
  • OrderServiceConfig.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes=OrderServiceConfig.class)public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testOrderService() { // test the orderService }}
  • @Configuration + XML Q: How can we combine @Configuration classes with XML config? A: Choose one as the entry point. Thats how it works in production anyway
  • Importing Configuration In XML: include @Configuration classes via component scanning or define them as normal Spring beans In an @Configuration class: use @ImportResource to import XML config files
  • Testing withEnvironment Profiles
  • @ActiveProfiles To activate bean definition profiles in tests...Annotate a test class with@ActiveProfilesSupply a list of profiles to beactivated for the testCan be used with @Configurationclasses and XML config
  • Lets look at an example with XML config...
  • app-config.xml (1/2)<beans ... > <bean id="transferService" class="com.example.DefaultTransferService"> <constructor-arg ref="accountRepository"/> <constructor-arg ref="feePolicy"/> </bean> <bean id="accountRepository" class="com.example.JdbcAccountRepository"> <constructor-arg ref="dataSource"/> </bean> <bean id="feePolicy" class="com.example.ZeroFeePolicy"/> <!-- dataSource --></beans>
  • app-config.xml (2/2)<beans ... > <!-- transferService, accountRepository, feePolicy --></beans>
  • app-config.xml (2/2)<beans ... > <!-- transferService, accountRepository, feePolicy --> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans></beans>
  • app-config.xml (2/2)<beans ... > <!-- transferService, accountRepository, feePolicy --> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> <beans profile="dev"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database> </beans></beans>
  • TransferServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • TransferServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/app-config.xml")public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • TransferServiceTest.java@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/app-config.xml")@ActiveProfiles("dev")public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • And now an example with @Configuration classes
  • TransferServiceConfig.java@Configurationpublic class TransferServiceConfig { @Autowired DataSource dataSource; @Bean public TransferService transferService() { return new DefaultTransferService(accountRepository(), feePolicy()); } @Bean public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } @Bean public FeePolicy feePolicy() { return new ZeroFeePolicy(); }}
  • } JndiDataConfig.java@Configuration@Profile("production")public class JndiDataConfig { @Bean public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); }}
  • StandaloneDataConfig.java@Configuration@Profile("dev")public class StandaloneDataConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); }}
  • And finally the test class...
  • TransferServiceTest.javapackage com.bank.service;@RunWith(SpringJUnit4ClassRunner.class)public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • TransferServiceTest.javapackage com.bank.service;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration( classes={ TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class})public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • TransferServiceTest.javapackage com.bank.service;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration( classes={ TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class})@ActiveProfiles("dev")public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService }}
  • Active Profile Inheritance@ActiveProfiles supports inheritance as wellVia the inheritProfiles attributeSee Javadoc for an example
  • ApplicationContext Caching
  • Until Spring 3.1
  • application contexts were cached
  • but using only resource locations for the key.
  • Now there are different requirements...
  • New Key Generation AlgorithmThe context cache key generation algorithm has been updated to include... locations (from @ContextConfiguration) classes (from @ContextConfiguration) contextLoader (from @ContextConfiguration) activeProfiles (from @ActiveProfiles)
  • SummaryThe Spring TestContext Framework simplifiesintegration testing of Spring-basedapplicationsSpring 3.1 provides first-class testing supportfor: @Configuration classes Environment profilesSee the Testing with @Configuration Classesand Profiles blog for further insight
  • Spring MVC Test Support
  • How Do You Test an @Controller?
  • @Controller@RequestMapping("/accounts")public class AccountController {// ... @ModelAttribute public Account getAccount(String number) { return this.accountManager.getAccount(number); } @RequestMapping(method = RequestMethod.POST) public String save(@Valid Account account, BindingResult result) { if (result.hasErrors()) { return "accounts/edit"; } this.accountManager.saveOrUpdate(account); return "redirect:accounts"; }}
  • Unit Test? Create controller instanceMock or stub services & repositories
  • Example@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account");}
  • Example@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); AccountManager mgr = createMock(AccountManager.class); mgr.saveOrUpdate(account); replay(mgr);}
  • Example@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); AccountManager mgr = createMock(AccountManager.class); mgr.saveOrUpdate(account); replay(mgr); AccountController contrlr = new AccountController(mgr); String view = contrlr.save(account, result);}
  • Example@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); AccountManager mgr = createMock(AccountManager.class); mgr.saveOrUpdate(account); replay(mgr); AccountController contrlr = new AccountController(mgr); String view = contrlr.save(account, result); assertEquals("redirect:accounts", view); verify(mgr);}
  • Example With Failure@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); result.reject("error.code", "default message")}
  • Example With Failure@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); result.reject("error.code", "default message") AccountManager mgr = createMock(AccountManager.class); replay(mgr);}
  • Example With Failure@Testpublic void testSave() { Account account = new Account(); BindingResult result = new BeanPropertyBindingResult(account, "account"); result.reject("error.code", "default message") AccountManager mgr = createMock(AccountManager.class); replay(mgr); AccountController contrlr = new AccountController(mgr); String view = contrlr.save(account, result); assertEquals("accounts/edit", view); verify(mgr);}
  • Not Fully Tested Request mappings Binding errors Type conversion Etc.
  • Integration Test? Selenium JWebUnit etc.
  • It Requires...A running servlet container More time to execute More effort to maintain Server is a black box
  • Actually...
  • its an end-to-end test
  • the only way to verify... Client-side behaviorInteraction with other server instances Redis, Rabbit, etc.
  • Wed also like to... Test controllers once! Fully & quickly Execute tests often
  • In summary...We cant replace the need for end-to- end tests But we can minimize errors Have confidence in @MVC code During end-to-end tests
  • Spring MVC Test Built on spring-test No Servlet container Drives Spring MVC infrastructureBoth server & client-side test support (i.e. RestTemplate code) Inspired by spring-ws-test
  • The Approach Re-use controller test fixturesI.e. mocked services & repositoriesInvoke @Controller classes through @MVC infrastructure
  • ExampleString contextLoc = "classpath:appContext.xml";String warDir = "src/main/webapp";MockMvc mockMvc = xmlConfigSetup(contextLoc) .configureWebAppRootDir(warDir, false) .build();mockMvc.perform(post("/persons")) .andExpect(response().status().isOk()) .andExpect(response().forwardedUrl("/add.jsp")) .andExpect(model().size(1)) .andExpect(model().hasAttributes("person"));
  • Under the CoversMock request/response from spring-test DispatcherServlet replacement Multiple ways to initialize MVC infrastructure Save results for expectations
  • Ways To Initialize MVC Infrastructure
  • From Existing XML Config// XML configMockMvc mockMvc = xmlConfigSetup("classpath:appContext.xml") .activateProfiles(..) .configureWebAppRootDir(warDir, false) .build();
  • From Existing Java Config// Java configMockMvc mockMvc = annotationConfigSetup(WebConfig.class) .activateProfiles(..) .configureWebAppRootDir(warDir, false) .build();
  • ManuallyMockMvc mockMvc = standaloneSetup(new PersonController()) .setMessageConverters(..) .setValidator(..) .setConversionService(..) .addInterceptors(..) .setViewResolvers(..) .setLocaleResolver(..) .build();
  • About Manual SetupMVC components instantiated directly Not looked up in Spring context Focused, readable testsBut must verify MVC config separately
  • TestContext Framework Example@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration( locations={"/org/example/servlet-context.xml"})public class TestContextTests { @Autowired private WebApplicationContext wac; @Before public void setup() { MockMvc mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac) .build(); }}
  • TestContext Framework Caches loaded Spring configuration Potentially across all tests!
  • However...WebApplicationContext not supported yet To be supported in Spring 3.2
  • In the mean time...You can use a custom ContextLoader Example exists in spring-test-mvc
  • Step 1 Add static imports MockMvcBuilders.*MockMvcRequestBuilders.*MockMvcResultActions.*
  • Easy to remember... MockMvc*
  • Also in Eclipse...Add MockMvc* classes in Preferences Java -> Editor -> Favorites Helps content assist
  • Step 2 Initialize MVC infrastructureString contextLoc = "classpath:appContext.xml";String warDir = "src/main/webapp";MockMvc mockMvc = xmlConfigSetup("classpath:appContext.xml") .configureWebAppRootDir(warDir, false) .build()// ...
  • Step 3 Build RequestString contextLoc = "classpath:appContext.xml";String warDir = "src/main/webapp";MockMvc mockMvc = xmlConfigSetup("classpath:appContext.xml") .configureWebAppRootDir(warDir, false) .build()mockMvc.perform(get("/").accept(MediaType.APPLICATION_XML))// ...
  • Step 4 Add ExpectationsString contextLoc = "classpath:appContext.xml";String warDir = "src/main/webapp";MockMvc mockMvc = xmlConfigSetup("classpath:appContext.xml") .configureWebAppRootDir(warDir, false) .build()mockMvc.perform(get("/").accept(MediaType.APPLICATION_XML)) .andExpect(response().status().isOk()) .andExpect(response().contentType(MediaType)) .andExpect(response().content().xpath(String).exists()) .andExpect(response().redirectedUrl(String)) .andExpect(model().hasAttributes(String...));// ...
  • Step 5 DebugString contextLoc = "classpath:appContext.xml";String warDir = "src/main/webapp";MockMvc mockMvc = xmlConfigSetup("classpath:appContext.xml") .configureWebAppRootDir(warDir, false) .build()mockMvc.perform(get("/").accept(MediaType.APPLICATION_XML)) .andPrint(toConsole());// ...
  • Demohttps://github.com/SpringSource/spring-test-mvc Sample Tests: org.springframework.test.web.server.samples
  • Client-Side ExampleRestTemplate restTemplate = ... ;MockRestServiceServer.createServer(restTemplate) .expect(requestTo(String)) .andExpect(method(HttpMethod)) .andExpect(body(String)) .andExpect(headerContains(String, String)) .andRespond(withResponse(String, HttpHeaders));
  • Project Availabilitygithub.com/SpringSource/spring-test-mvc Request for feedback! Send comments Pull requests
  • Nightly Snapshot Available<repository> <id>org.springframework.maven.snapshot</id> <name>Spring Maven Snapshot Repository</name> <url>http://maven.springframework.org/snapshot</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots></repository>
  • In Closing ...
  • This PresentationSource:https://github.com/rstoyanchev/spring-31-and-mvc-testing-supportSlides:http://rstoyanchev.github.com/spring-31-and-mvc-test
  • Resources for Spring MVC TestProject Home:https://github.com/SpringSource/spring-test-mvcSample Tests:org.springframework.test.web.server.samples
  • Resources for Core SpringSpring Framework:http://www.springsource.org/spring-coreReference Manual: Spring 3.1 RC1Forums: http://forum.springframework.orgJIRA: https://jira.springsource.orgSpring in a Nutshell … available in 2012
  • Q&A Sam Brannen Web: Swiftmind.com Twitter: @sam_brannenRossen Stoyanchev Web: SpringSource.com Twitter: @rstoya05