Next
Testing
You can’t fix what you can’t run
You can’t fix what you can’t debug
You can’t fix what you can’t test
You can’t develop if you can’t test
Testing is development
@Inject
@CreditCard
Payment payment; (1)
1 | CDI injects instance |
PaymentGateway pg = new PaymentGateway();
pg.setPayment(new CreditCardPayment()); (1)
pg.doPayment(...);
1 | Our test is not executed within CDI container |
Next
Principles of Arquillian
1 Portable tests
2 Executable from IDE and the build tool
3 Reuse existing framework and tools
4 Flexbible to adapt to new technologies
5 Extensible to integrate with new plataformas
6 Facilitate deployment of application
public class PaymentGatewayTest {
PaymentGateway paymentGateway;
@Test
public void should_do_credit_card_payments() {
paymentGateway = new PaymentGateway();
paymentGateway.setPayment(new CreditCardPayment());
assertThat(paymentGateway.doPayment(...), is(...));
}
}
@RunWith(Arquillian.class)
public class PaymentGatewayTest {
@Deployment public static JavaArchive createDeployment() {
return Shrinkwrap.create(JavaArchive.class)
.addClasses(PaymentGateway.class,
CreditCardPayment.class, CreditCard.class);
}
@Inject PaymentGateway paymentGateway;
@Test
public void should_do_credit_card_payments() {
assertThat(paymentGateway.doPayment(...), is(...));
}
}
<dependencyManagement>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.5.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencyManagement>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
@RunWith(Arquillian.class)
public class PaymentGatewayTest {
@Deployment public static JavaArchive createDeployment() {
return Shrinkwrap.create(JavaArchive.class)
.addClasses(PaymentGateway.class,
CreditCardPayment.class, CreditCard.class);
}
@Inject PaymentGateway paymentGateway;
@Test
public void should_do_credit_card_payments() {
assertThat(paymentGateway.doPayment(...), is(...));
}
}
<profile>
<id>wildlfy-embedded</id>
<dependencies>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-arquillian-container-embedded</artifactId>
<version>8.1.0.Final</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>tomee-embedded</id>
<dependencies>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>arquillian-tomee-embedded</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
</profile>
ShrinkWrap.create(JavaArchive.class)
.addClasses(PaymentGateway.class)
.addPackages(CreditCardPayment.class.getPackage());
ShrinkWrap.create(WebArchive.class)
.addAsLibraries(x)
.addAsWebInfResource(
new StringAsset("<faces-config version=\"2.0\"/>"),
"faces-config.xml")
.setWebXML(new File("src/test/resources/web.xml"));
ShrinkWrap.create(EnterpriseArchive.class)
.addAsModules(war, jar);
File[] files = Maven.resolver()
.resolve("G:A:V").withTransitivity()
.asFile();
Descriptors.create(WebAppDescriptor.class)
.metadataComplete(true)
.version("3.0")
.createServlet()
.servletName(EchoServlet.class.getSimpleName())
.servletClass(EchoServlet.class.getName()).up()
.createServletMapping()
.servletName(EchoServlet.class.getSimpleName())
.urlPattern(EchoServlet.URL_PATTERN).up()
.exportAsString()
Next
Arquillian Extensions
@Before
public void preparePersistenceTest() {
clearDatabase();
insertData();
startTransaction();
}
private void clearDatabase() {
utx.begin();
em.joinTransaction();
em.createQuery("delete from Beer").executeUpdate();
utx.commit();
}
private void insertData() {
utx.begin();
em.joinTransaction();
Beer beer = BeerBuilder.create("Heineken").style("Pale Lager").build();
em.persist(beer);
utx.commit();
}
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-persistence-dbunit</artifactId>
<version>${arquillian-persistence.version}</version>
<scope>test</scope>
</dependency>
beers.yaml
----
beer:
- id: 1
name: Heineken
style: Pale Lager
----
@EJB BeerService beerService;
@Test
@UsingDataSet("datasets/beers.yml")
public void should_return_beer_by_type() throws Exception {
assertThat(beerService.findBeersByType("Pale Lager"), hasSize(1));
}
<dependencyManagement>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>
<version>${drone.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.selenium</groupId>
<artifactId>selenium-bom</artifactId>
<version>${selenium.bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-webdriver-depchain</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<version>2.0.3.Final</version>
<scope>test</scope>
<type>pom</type>
</dependency>
<arquillian>
<extension qualifier="webdriver">
<property name="browser">firefox</property>
</extension>
</arquillian>
@Deployment(testable = false)
public static WebArchive createDeployment() {}
@ArquillianResource
URL contextPath;
@Drone
WebDriver driver;
@Test
public void should_show_all_sessions(@InitialPage ShowSessionPage showSessionPage) throws IOException {
showSessionPage.assertSessionsAreShown();
}
<div class="jumbotron">
<div id="welcomeMessage" class="content">
<h1>List of sessions registered on system</h1>
</div>
</div>
<div class="well well-sm">
<div class="panel panel-default">
<h:dataTable id="session" styleClass="table table-striped"
value="#{sessionService.sessions}" var="sessionConference">
<h:column>
<f:facet name="header">Title</f:facet>
<h:outputText styleClass="title" value="#{sessionConference.title}" />
</h:column>
<h:column>
<f:facet name="header">Start</f:facet>
#{sessionConference.startDate}
</h:column>
</h:dataTable>
</div>
</div>
@Location("view.xhtml")
public class ShowSessionPage {
@FindBy
private WebElement welcomeMessage;
@FindBy
private WebElement session;
public void assertSessionsAreShown() {
assertThat(sessionTitles(), containsInAnyOrder("Devoxx", "JavaOne"));
}
}
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-angularjs-graphene</artifactId>
<version>1.2.0.Beta1</version>
</dependency>
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
<span>{{remaining()}} of {{todos.length}} remaining</span>
[ <a href="" ng-click="archive()">archive</a> ]
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form ng-submit="addTodo()">
<input type="text" ng-model="todoText" size="30" placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
@FindByNg(model = "todo.done") List<WebElement> todos;
@FindByNg(model = "todoText") WebElement todoEntry;
@FindByNg(action = "addTodo()") WebElement addTodo;
@FindByNg(repeat = "todo in todos") List<WebElement> todoRepeat;
@Test
public void testAddTodo() {
assertEquals(2, todos.size());
todoEntry.sendKeys("This is a new TODO item");
addTodo.submit();
assertEquals(3, todos.size());
}
<dependency>
<groupId>com.github.cukespace</groupId>
<artifactId>cukespace-core</artifactId>
<version>1.5.10</version>
<scope>test</scope>
</dependency>
Feature: Find Conferences
Scenario: Finding All Conferences
Given I have Devoxx conference
When I find all conferences in system
Then Devoxx should be listed
@RunWith(CukeSpace.class)
@Features(value = "org/superbiz/conferences-service.feature")
public class ConferenceServiceFuntionalTest {
@Deployment public static JavaArchive deploy() { }
@EJB ConferenceService conferenceService;
@Given(value = "^I have (\\w+) conference$")
public void aConference(String conferenceName) {}
@When(value = "^I find all conferences in system$")
public void findAllConferences() {}
@Then(value = "^(\\w+) should be listed$")
public void shouldFindConferences(String conferenceName) {}
}
<dependency>
<groupId>org.jboss.arquillian.spock</groupId>
<artifactId>arquillian-spock-container</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
<version>${version.spock}</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<scope>test</scope>
<version>${version.groovy}</version>
</dependency>
@RunWith(ArquillianSputnik)
class AccountServiceSpecification extends Specification {
@Deployment def static JavaArchive "create deployment"() {}
@Inject AccountService service
def "transfer should be possible between two accounts"() {
when:
service.transfer(from, to, amount)
then:
from.balance == fromBalance
to.balance == toBalance
}
Next
Conclusions
The purpose of automated testing is to enable change.
Verifying correctness is just a nice side-effect.
Jeremy Norris
Testing with mocks only proves you know how to write mocks
Dan Allen
Write Real tests