Monday, August 15, 2016


[INFO] Reactor Summary:
[INFO] 
[INFO] ix-checkit-dc ..................................... SUCCESS [0.003s]
[INFO] ix-checkit-contract ............................... SUCCESS [1.642s]
[INFO] ix-checkit-frontend: jquery, spring mvc ........... SUCCESS [2.345s]
[INFO] ix-checkit-backend: spring data rest, psql/h2 ..... SUCCESS [6.165s]
[INFO] ix-checkit-test: selenium IT ...................... SUCCESS [19.118s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

The road to hell is paved with good intentions


Motivation

  • During TDD there is a risk to break contracts between different components represented with war files
  • It is useful to add a separate test project to run lightweight multi war integration tests to check the whole application integrity. These tests should be executed during common checkin build process as a part of continuous integration

Example application includes

  • Backend war project, implemented with spring data rest. It has its own TDD tests implemented with mockMvc. There is no need in container, just mockMvc, to test the rest-services representing db entities. Test db is H2
  • Front-end war project, implemented with spring MVC, and jquery. It has its own TDD tests implemented with Mockito.
  • Selenium test project. Integration tests use jetty to deploy front-end and to test jsp pages. There is only one instance of jetty is used to deploy front-end. Backend mockMvc is used to test backend and to provide data for front-end. MockMvc instance is created in the context of the test itself and then it is passed to front-end, deployed in jetty context, through the dedicated static field of the front-end test (fake) service. So the Selenium test project is lightweight and can be run during the build process.
  • Spring annotation configuration.

Test example


@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = { BEAppConfigurationDev.class,
 BEWebConfigurationDev.class })

@ActiveProfiles("dev")

@Transactional

@WebAppConfiguration

public class CheckItSeleniumIT { 

protected MockMvc backEndMockMvc;

@Autowired
  private WebApplicationContext backEndWebApplicationContext;

@Autowired
  private ItemService itemService;

@Before 
  public void setUp() throws Exception, BEError { 
    prepareSeleniumDriver();  check next pic. for details 
    createBackEndMockMvcAndPassItToJettyContext();
    prepareAndStartJetty();
  }

@Test
  public void testCheckItGoToEditPage() throws Exception {
    load_checkIt_homePage();
    when_double_click_on_first_row_of_itemGrid_Then_go_to_edit_page();
    when_an_option_entered_Then_it_is_in_the_option_grid();
  }

  private void load_checkIt_homePage() {
    seleniumDriver.get(seleniumBaseUrl + "/items");
  }

  private void
when_double_click_on_first_row_of_itemGrid_Then_go_to_edit_page() { WebElement row = (new WebDriverWait(seleniumDriver, 10)) .until(ExpectedConditions.presenceOfElementLocated( By.cssSelector("table[id='list'] tr[id='1']"))); Actions builder = new Actions(seleniumDriver); Action doubleClick = builder.moveToElement(row).doubleClick().build(); doubleClick.perform(); } private void
when_an_option_entered_Then_it_is_in_the_option_grid() { seleniumDriver.findElement(By.id("itemTextInput222")).clear(); seleniumDriver.findElement(By.id("itemTextInput222")) .sendKeys("aaabbbccc"); seleniumDriver.findElement(By.name("itemOption.itemComments")) .clear(); seleniumDriver.findElement(By.name("itemOption.itemComments")) .sendKeys("dddeeefff"); seleniumDriver.findElement(By.cssSelector( "#addOption > div.btBox > button[type=\"submit\"]")).click(); seleniumDriver.findElement(By.cssSelector("span[title=\"dddeeefff\"]")) .click(); WebElement itemText = (new WebDriverWait(seleniumDriver, 10)) .until(ExpectedConditions.presenceOfElementLocated( By.cssSelector("span[title=\"dddeeefff\"]"))); assertEquals("aaabbbccc",itemText.getText()); }

setUp details


  private void prepareSeleniumDriver() {
    seleniumDriver = new ChromeDriver();
  }

  private void createBackEndMockMvcAndPassItToJettyContext() {
    backEndMockMvc = webAppContextSetup(backEndWebApplicationContext)
.build(); itemService.setMockMvc(backEndMockMvc); } private void prepareAndStartJetty() throws Exception { createJetty(); initJettyServerConnector(); createJettyContextAndStart(); } private void createJetty() { jetty = new Server(8080); jetty.setStopAtShutdown(true); } private void initJettyServerConnector() { ServerConnector scc = new ServerConnector(jetty); scc.setPort(8080); jetty.setConnectors(new Connector[] { scc }); } private void createJettyContextAndStart() throws Exception { WebAppContext context = createAndConfigureJettyContext(); jetty.setHandler(context); jetty.start(); } private WebAppContext createAndConfigureJettyContext() { WebAppContext context = createJettyContext(); configureJettyContextWithFrontEndInitializer(context); return context; } private WebAppContext createJettyContext() { WebAppContext context = new WebAppContext(); context.setServer(jetty); context.setResourceBase(
"target/war/work/ru.ix.item.ui/ix-checkit-frontend"); context.setContextPath("/ix-checkit-frontend"); return context; } private void
configureJettyContextWithFrontEndInitializer(WebAppContext context) { context.setConfigurations(new Configuration[]{ new WebXmlConfiguration(), new AnnotationConfiguration() { @Override public void preConfigure(WebAppContext context) throws
Exception { final ClassInheritanceMap map = new ClassInheritanceMap(); final ConcurrentHashSet set = new ConcurrentHashSet<>(); set.add(FEWebAppInitializerDev.class.getName()); map.put(WebApplicationInitializer.class.getName(), set); context.setAttribute(CLASS_INHERITANCE_MAP, map); _classInheritanceHandler = new ClassInheritanceHandler(
map); } } }); }

Summary

Once upon a time I participated in some interesting project. There was a lot of war based components. There was a dedicated testing team. There was a lot of tests, all war component interactions had test-automation stuff, but one external link with CRM component. This link, as it was found after tdd had been completed, was the most important.