Loading

Help documentation

Initialize Tests Using Spring

Twist uses the Spring Inversion of Control (IOC) container for managing dependencies. Using an IOC container makes it possible to manage the life-cycle of objects and resources outside the scenarios. For example, the IOC container makes it easy to share a browser instance across multiple tests. Resources that are expensive to create, and that can be utilized across multiple tests, are ideal candidates for injection via IOC.

Please refer to the bundled example - Web Application Testing With Sahi (uses Mingle) to see an example of how Spring is configured for use with 'Sahi' as driver.

Resources are managed at two levels (containers):

Suite Level Container

The Suite Level Container is created and destroyed once during the entire test run. This can be used to create and inject resources that are required across all the test runs . For example, if you would like a browser instance to be reused across a test suite, the suite level container would be a good place to create it. This container can be configured using applicationContext-suite.xml.

Scenario level container

The Scenario Level Container is created and destroyed each time a scenario is executed. This container can be used to create and inject resources and classes that are required during the execution of every scenario. This container can be configured using applicationContext-scenario.xml.

Twist loads applicationContext-suite.xml and applicationConext-scenario.xml from the project's classpath.

Examples

Here is an example of how to use different kinds of beans to suit your needs. This allows you to initialize objects that can setup your test environment for you.

The following configuration allows your Workflows to use objects initialized in the container:

public class Login {

  private WebDriver browser;
  private Authentication auth;
  
  public Login(WebDriver browser, Authentication auth) {
    this.browser = browser;
    this.auth = auth;
  }
  
}

There is an exhaustive list of all spring configuration directives available at the Spring website.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation=
        "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <!-- ========================================================== -->
  <!-- SimpleBean should have a default no-args constructor. -->
  <!-- ========================================================== -->
  <bean id="simpleBean" class="com.foo.example.container.SimpleBean"/>

  <!-- ========================================================== -->
  <!-- An example of a bean that needs some constructor parameters -->
  <!-- ========================================================== -->
  <bean id="initializedUsingConstructor" class="com.foo.example.container.Authentication">
    <constructor-arg type="java.lang.String" value="username"/>
    <constructor-arg type="java.lang.String" value="password"/>
  </bean>
  
  <!-- ========================================================== -->
  <!-- You can also have a bean that depends on another bean, via it's constructor -->
  <!-- ========================================================== -->
  <!--
    public class NeedsSimpleBean {
      public NeedsSimpleBean(SimpleBean simpleBean) {
        // do something with simpleBean
      }
    }
  -->
  <bean id="beanDependingOnSimpleBean" class="com.foo.example.container.NeedsSimpleBean" autowire="constructor"/>
  
  <!-- ========================================================== -->
  <!-- beans can be initialized using properties from property files -->
  <!-- ========================================================== -->
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="locations">
      <value>classpath:twist.properties</value>
    </property>
  </bean>

  <!-- ========================================================== -->
  <!-- You can also set system properties as part of initialization -->
  <!-- ========================================================== -->
  <bean id="systemproperty" class="com.thoughtworks.twist.core.SystemPropertyInitializingBean">
    <property name="systemProperties">
      <map>
        <entry key="http.proxyHost" value="your.proxy.host"/>
        <entry key="http.proxyPort" value="8080"/>
        <entry key="http.proxyUser" value="proxyUser"/>
        <entry key="http.proxyPassword" value="proxyPassword"/>
      </map>
    </property>
  </bean>

  <!-- ========================================================== -->
  <!-- A bean can be created using a factory as well. -->
  <!-- ========================================================== -->
  <!-- Create and initialize the factory that'll create the bean. -->
  
  <bean id="webDriverFactory" class="com.thoughtworks.twist.driver.webdriver.WebDriverFactory"
        init-method="start" destroy-method="stop" lazy-init="true">
        <property name="enableNativeEvents" value="${webdriver.browser.nativeEvents}"/> 
        <property name="javascriptEnabled" value="${webdriver.browser.javascriptEnabled}"/> 
        <property name="htmlUnitBrowserVersion" value="${webdriver.browser.version}"/> 
        <property name="scriptPath" value="${webdriver.browser.script}"/> 
        <property name="profilePath" value="${webdriver.browser.profile}"/> 
    </bean>
    
    <!-- Create the bean using the factory 'webDriverFactory' -->
    <bean id="browser" factory-bean="webDriverFactory" factory-method="getBrowser"
        lazy-init="true" destroy-method="quit" scope="singleton">
        <constructor-arg value="${webdriver.browser}" />
    </bean>
  
  
  <!-- ========================================================== -->
  <!-- Use a database to get or set test data. -->
  <!-- ========================================================== -->
  <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>

</beans>

The bean definitions have some additional attributes:
init-methodCalled after the bean is created in order to initialize it
destroy-methodCalled before the bean needs to be destroyed (typically when the container shuts down)
lazy-initIf this is set to 'true', the bean will be created only when it is required, setting this to false will mean that the bean is created when the container starts.

Annotation based configuration

You can also take advantage of annotation based configuration by annotating component class, method or field declaration. For details you can refer to Spring 3 documentation here. Using @Autowired annotation, you can autowire relationships between collaborating beans. You can also leverage Spring's stereotype annotations like @Component and automatically detect and register bean definitions. For example if you have a test Fixture class "Search" like below,

import org.example;

public class Login {

  private WebDriver browser;
  
  @Autowired
  private ApplicationConfig config;

  public Search(WebDriver browser) {
    this.browser = browser;
  }
  
  public void loginToApplication() throws Exception {
    browser.findElement(By.id("user_name")).sendKeys(config.getUsername());
    browser.findElement(By.id("user_password")).sendKeys(config.getPassword());
    browser.findElement(By.name("login")).submit();
  }
  
}  
You can autowire the instance variable using @Autowired annotation. The actual bean definition of the example "ApplicationConfig" class can either be done using usual XML based configuration or annotating with @Component stereotype like below:
package org.example;

import org.springframework.stereotype.Component;

@Component
public class ApplicationConfig {
  public String getUsername() {
    return "admin";
  }
  
  public String getPassword() {
    return "admin";
  }
}  
To automatically detect the above component and register, you can configure where the base package of the component class is, either in applicationContext-suite.xml or applicationContext-scenario.xml like below.
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation=
        "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
        
        <context:annotation-config/> 
        <context:component-scan base-package="org.example"/> 
</beans>