arquillian in a nutshell

17
Preface Targeted audience Get the code Why Arquillian? The first test Creating the archive Create the archive using ShrinkWrap Create the archive using maven Configuring Arquillian Configuring the runtime modes of arquillian Managed container config Embedded container config Including persistency (extension) Creating test data manually Creating test data by SQL script Creating test data by means of JSON/XML (or YML) Some kind of round trip Copyright and all intellectual property belongs to Brockhaus Group 1

Upload: brockhaus-group

Post on 22-May-2015

485 views

Category:

Software


4 download

DESCRIPTION

A 12 pager describing how to get Arquillian up an running.

TRANSCRIPT

Page 1: Arquillian in a nutshell

Preface Targeted audience Get the code

Why Arquillian? The first test

Creating the archive Create the archive using ShrinkWrap Create the archive using maven

Configuring Arquillian Configuring the runtime modes of arquillian

Managed container config Embedded container config

Including persistency (extension) Creating test data manually Creating test data by SQL script Creating test data by means of JSON/XML (or YML) Some kind of round trip

Copyright and all intellectual property belongs to Brockhaus Group 1

Page 2: Arquillian in a nutshell

Preface This is a paper provided by Brockhaus Group for free. All content was checked and all code was tested carefully; in case of questions of suggestions to improve we will be happy to receive you email at:

[email protected]

Targeted audience People interested in using Arquillian with a sound knowledge in Java EE technologies, this paper won’t explain the details of the Java EE platform. The examples have been tested using JBoss EAP 6.1.1 and the H2 database.

Get the code All code used for this examples can be found here (zipped Maven project):

● ftp:www.brockhaus-gruppe.de ● /getKnowledge/arquillian ● user: ftpguest ● pwd: ftpguest789

Download, unpack, you’re done

Copyright and all intellectual property belongs to Brockhaus Group 2

Page 3: Arquillian in a nutshell

Why Arquillian? Testing of complex. multi-layered Java EE applications still is pretty difficult as in many cases the services offered by the container needs to be in place. Just think of CDI, various annotations, datasources and so on. One might say, that mocking might replace these concerns and partially this is correct but still there is a gap between what is provided and what is expected. Arquillian tries to close this gap by:

● maintaining the life-cycle of of a container (and despite the fact Arquillian is a JBoss project there are more containers supported than just JBoss ) 1

● combining all resources to a deployable artifact and providing some facilities to deploy these

Several plugins are available:

● Drone (includes Selenium to test the UI as well) ● Persistence (to create test data out of various formats like XML and JSON) ● Jacoco (to get some metrics about code coverage)

The first test At first sight an arquillian test doesn’t differ much from a regular unit test:

@RunWith(Arquillian.class) public class CalculatorArquillianTest { // must be @EJB, can't be @Inject! If you don't believe, you can try ... @EJB private Calculator calc; // the one you need under any circumstance @Deployment public static Archive<?> createTestArchive() { // check the helper for details of how to get the things packaged Archive<?> archive = ArchiveHelper.getArchive(); return archive; } @Test public void testAddNumbers() { float result = calc.add(1, 2); Assert.assertEquals(3, result, 0); } }

The only remarkable things are the @RunWith(Arquillian.class) and the @Deployment annotations. Regarding the @RunWith annotation we should not spent too many words, The @Deployment annotation looks much more interesting as it seems as if some groundwork is laid in there.

1 This paper will cover JBoss EAP 6.1.1 only

Copyright and all intellectual property belongs to Brockhaus Group 3

Page 4: Arquillian in a nutshell

Creating the archive One of the main purposes of the method annotated with @Deployment is to provide the artifact to be tested. // the one you need under any circumstance @Deployment public static Archive<?> createTestArchive() { // check the helper for details of how to get the things packaged Archive<?> archive = ArchiveHelper.getArchive(); return archive; } We have made use of some kind of helper to create the archive as there are two options which will be explained in more detail.

Create the archive using ShrinkWrap Using this handy tool, any JavaEE deployable artifact can be created on the fly. /** doing it the hard way ... guess you won't like it as EVERY class plus related stuff needs to be specified */ private static Archive<?> getArchiveManually() { // creating archive manually JavaArchive artifact =

ShrinkWrap.create(JavaArchive.class, ARCHIVE_NAME) .addPackage(Calculator.class.getPackage()) .addPackage(CalculatorService.class.getPackage()) .addPackage(FooServiceBean.class.getPackage()) .addPackage(Foo.class.getPackage()) .addPackage(FooService.class.getPackage()) .addAsResource("META-INF/persistence.xml") .addAsResource("META-INF/beans.xml"); // so we might write it for further inspection if (WRITE_ARCHIVE) { artifact.as(ZipExporter.class) .exportTo(new File("D:/Projekte/ffz/tmp/" + ARCHIVE_NAME), true); } return artifact; }

As you can see easily, every class, deployment descriptor and so on is added on the fly. For your convenience the code to write the created archive to the file system is included. This was proven helpful just to check, whether everything is included properly. IMHO the only disadvantage might be the vast amount of classes in an average project so we were pretty confident there must be another way of getting the archive.

Create the archive using maven

Copyright and all intellectual property belongs to Brockhaus Group 4

Page 5: Arquillian in a nutshell

One of the de-facto standards in build tools is maven, so why not making use of the famous mvn clean package to get the things done? The following snippet describes how to get the archive loaded into arquillian. /** maven did it for us .. we just have to read the file */ private static Archive<?> getArchiveFromFile() { JavaArchive artifact = ShrinkWrap .create(ZipImporter.class, ARCHIVE_NAME) .importFrom(ARCHIVE_FILE) .as(JavaArchive.class); return artifact; }

Copyright and all intellectual property belongs to Brockhaus Group 5

Page 6: Arquillian in a nutshell

Configuring Arquillian The initial thing we should tell Arquillian is where to find JBoss, therefore a small xml file named arquillian.xml needs to put aside of your application. <?xml version="1.0"?> <arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <defaultProtocol type="Servlet 3.0" /> <!-- for additional info see: https://docs.jboss.org/author/display/ARQ/Container+configuration --> <container qualifier="jboss" default="true"> <configuration> <!-- this points to an existing installation, take care for everything to be set up properly, esp. the datasources --> <property name="jbossHome">D:\abc\xyz\jboss-eap-6.1</property> </configuration> </container> </arquillian> Be aware of JBoss configured according to your needs, no topic or queue, no data source means no topic or queue or data source available at testing time. The runtime selection is explained in more detail here.

Copyright and all intellectual property belongs to Brockhaus Group 6

Page 7: Arquillian in a nutshell

As we suppose you’re making use of maven, here are the dependencies you will need to make the test run: <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>6.0</version> </dependency> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>3.0.2.Final</version> <type>pom</type> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>xalan</groupId> <artifactId>xalan</artifactId> <version>2.7.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <version>1.1.2.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.protocol</groupId> <artifactId>arquillian-protocol-servlet</artifactId> <version>1.1.2.Final</version> <scope>test</scope> </dependency> <!-- if not using any profile --> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-arquillian-container-managed</artifactId> <version>7.2.0.Final</version> </dependency> Yu might want to check the poms included in the zip files ...

Copyright and all intellectual property belongs to Brockhaus Group 7

Page 8: Arquillian in a nutshell

Now you are almost ready to run your first Arquillian test (mvn clean test / or using eclipse).

Configuring the runtime modes of arquillian In the very beginning we should have made the decision of how to use Arquillian as several modes are supported:

● Remote container Will run in a separate VM, probably on a remote machine.

● Managed container Arquillian starts and stops the container (which must be installed locally).

● Embedded container Will run in the same VM, Arquillian will manager the container.

Using JBoss, all modes need a JBoss to be installed and localized by arquillian.xml. We suggest the following approach: Make use of maven to build your archives and include the necessary dependencies within the POM. Whatever mode you might choose, put the relevant configuration into a decent profile. We have tested the managed and embedded option only.

Managed container config … within <profiles> </profiles> tags obviously: <profile> <id>arqillian-jbossas-managed</id> <dependencies> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-arquillian-container-managed</artifactId> <version>7.2.0.Final</version> <scope>test</scope> </dependency> </dependencies>

Copyright and all intellectual property belongs to Brockhaus Group 8

Page 9: Arquillian in a nutshell

</profile>

Embedded container config <!-- for embedded option see: https://community.jboss.org/thread/236562 quoted: The only thing Embedded does is to start up the Container within the same JVM. It still needs to know where the jars/configuration is, via jbossHome (see arquillian.xml). --> <profile> <id>arquillian-jbossas-embedded</id> <dependencies> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-arquillian-container-embedded</artifactId> <version>7.2.0.Final</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <systemPropertyVariables> <!-- needs to be defined for the embedded option --> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> </systemPropertyVariables> </configuration> </plugin> </plugins> </build> </profile> See the embedded config running using maven (mvn clean test -Parquillian-jbossas-embedded)

Copyright and all intellectual property belongs to Brockhaus Group 9

Page 10: Arquillian in a nutshell

If you want to make an embedded test run from eclipse you have to add a system property or configure properly within the POM:

Copyright and all intellectual property belongs to Brockhaus Group 10

Page 11: Arquillian in a nutshell

Copyright and all intellectual property belongs to Brockhaus Group 11

Page 12: Arquillian in a nutshell

Including persistency (extension) The full monty of integration testing is provided using persistency. In the field of testing, persistency first of all means generating test data for later use or testing the creation of data (which is almost synonymous). There are several options to get this task done, namely:

● create test data manually using SQL scripts and a pre-filled database. ● create/delete test data manually within the @Before annotated method ● create/delete test data by SQL script ● create/delete test data by means of XML / JSON

Creating test data manually using a pre-filled database An easy task, just make use of the following: INSERT INTO FOO (ID, CREATIONTIME , DESCRIPTION) VALUES(998, '2014-02-18 12:37:06.73', 'FooBarOne');

INSERT INTO FOO (ID, CREATIONTIME , DESCRIPTION) VALUES(999, '2014-02-18 12:37:06.73', 'FooBarTwo');

Creating test data manually The most obvious way of creating (and deleting) test data during a test is doing so within the setUp() and tearDown() methods of a test : 2

@RunWith(Arquillian.class) public class FooServiceArquillianTest { @EJB private FooService fooService; private Foo foo = new Foo(); @Deployment public static Archive<?> createTestArchive() { Archive<?> archive = ArchiveHelper.getArchive(); return archive; }

@Before public void setUp() { foo.setCreationTime(new Date(System.currentTimeMillis())); foo.setDescription("Foo Bar"); foo.setCreationTime(new Date(System.currentTimeMillis()));

foo = fooService.createFoo(foo); } @Test public void testFindAll() { List<Foo> hits = fooService.findAllFoos(); Assert.assertTrue(hits.size() > 0); }

2 more precisely: the methods annotated by @BeforeXxx and @AfterXxx

Copyright and all intellectual property belongs to Brockhaus Group 12

Page 13: Arquillian in a nutshell

@Test public void testFindFoosEarlierThan() { Date future = new Date(System.currentTimeMillis() + 100000); List<Foo> hits = fooService.findFoosEarlierThan(future); Assert.assertTrue(hits.size() > 0); }

@After public void tearDown() { List<Foo> hits = fooService.findAllFoos(); for (Foo foo : hits) {

fooService.deleteFoo(foo); } } }

Obviously this approach only works well for not-so-complicated objects and associations.

Creating test data by SQL script Maybe your individual preferences are with SQL and so you want to get the things done by a customized SQL script. Arquillian supports in doing so by annotations like @ApplyScriptBefore and @CreateSchema. We will focus on the first one as we have left schema creation to JPA/Hibernate.

WARNING: At the time of writing this paper, we have the Alpha6 release of the framework mentioned, there are several changes even in the names of the annotations which might drive you crazy. Even the Arquillian book available is not up to date. Don’t forget to include the extensions into your POM: <dependency> <groupId>org.jboss.arquillian.extension</groupId> <artifactId>arquillian-persistence-api</artifactId> <version>1.0.0.Alpha6</version> </dependency>

<dependency> <groupId>org.jboss.arquillian.extension</groupId> <artifactId>arquillian-persistence-impl</artifactId> <version>1.0.0.Alpha6</version> </dependency>

Presume we have the following script:

INSERT INTO FOO (ID, CREATIONTIME , DESCRIPTION) VALUES(998, '2014-02-18 12:37:06.73', 'FooBarOne'); INSERT INTO FOO (ID, CREATIONTIME , DESCRIPTION) VALUES(999, '2014-02-18 12:37:06.73', 'FooBarTwo');

Put the script into the /src/test/resources/scripts folder of your maven project and annotate the class accordingly:

Copyright and all intellectual property belongs to Brockhaus Group 13

Page 14: Arquillian in a nutshell

@RunWith(Arquillian.class)

@ApplyScriptBefore("scripts/CreateFoo.sql") public class FooServiceArquillianSQLScriptTest { @EJB private FooService fooService; @Deployment public static Archive<?> createTestArchive() { Archive<?> archive = ArchiveHelper.getArchive(); return archive; } @Test public void testFindAll() { List<Foo> hits = fooService.findAllFoos(); Assert.assertTrue(hits.size() > 0); } @Test public void testFindFoosEarlierThan() { Date future = new Date(System.currentTimeMillis() + 100000); List<Foo> hits = fooService.findFoosEarlierThan(future); Assert.assertTrue(hits.size() > 0); } }

Everything should work fine now … Obviously this model works fine for not-so-complicated objects and associations. If complex objects respective their associations exist, there might be better options.

Creating test data by means of JSON/XML (or YML) Presumed, the Foos are described using JSON, one can import them easily using a specific notation. The whole thing is based upon DBUnit. The JSON file (preferably stored in /src/test/resources/datasets):

{ "foo" : [ { "id" : 998, "description" : "Foo One", "creationTime" : "2014-02-19 09:31:33" }, { "id" : 999, "description" : "Foo Two", "creationTime" : "2014-02-19 09:31:33" } ] }

The test itself:

@RunWith(Arquillian.class)

@UsingDataSet("datasets/Foos.json")

Copyright and all intellectual property belongs to Brockhaus Group 14

Page 15: Arquillian in a nutshell

public class FooServiceArquillianDataSetTest { @EJB private FooService fooService; @Deployment public static Archive<?> createTestArchive() { Archive<?> archive = ArchiveHelper.getArchive(); return archive; } @Test public void testFindAll() { List<Foo> hits = fooService.findAllFoos(); Assert.assertTrue(hits.size() > 0); } @Test public void testFindFoosEarlierThan() { Date future = new Date(System.currentTimeMillis() + 100000); List<Foo> hits = fooService.findFoosEarlierThan(future); Assert.assertTrue(hits.size() > 0); } }

Some kind of round trip Maybe you consider it a bright idea to serialize your object in JSON and to load these objects - maybe after some changes - into the database or utilize them as test data. Well there are several options available to do so, the most popular frameworks are Google’s gson and Jackson. The following explains how to do so using Jackson but keep in mind, there is still a lot to discover. To convert / serialize your objects some kind of utility class might be of help:

public class JSONParser { /** self reference */ private static final JSONParser THIS = new JSONParser(); private ObjectMapper objectMapper = new ObjectMapper(); // Singleton private JSONParser() { // see: http://wiki.fasterxml.com/JacksonFAQDateHandling DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); objectMapper.setDateFormat(df); objectMapper.getDeserializationConfig().withDateFormat(df); } public static JSONParser getInstance() { return THIS; } public String toJSON(Object o) { String ret = null; try { ret = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);

Copyright and all intellectual property belongs to Brockhaus Group 15

Page 16: Arquillian in a nutshell

} catch (JsonGenerationException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ret; } public <T> T fromJSON(Class<T> clazz, String json) { T ret = null; try { ret = objectMapper.readValue(json, clazz); } catch (JsonParseException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ret; } }

This class serves perfectly as long as you want to serialize just one foo, if you want to serialize more of them, you need a so-called wrapper class (note the field name of the collection, it’s a singular, leave it like this to get the expected result):

public class FooWrapper { List<Foo> foo = new ArrayList<Foo>(); public List<Foo> getFoo() { return foo; } public void setFoo(List<Foo> foo) { this.foo = foo; } }

Creating the JSON representation is simple like this (think of combining it with a findAll() method of the DAO layer):

public class JSONParserTest { public static void main(String[] args) { JSONParserTest test = new JSONParserTest(); test.serializeFoos(); } private void serializeFoos() { FooWrapper wrapper = new JSONParserTest.FooWrapper(); Foo foo1 = new Foo(); foo1.setId(998l); foo1.setCreationTime(new Date(System.currentTimeMillis())); foo1.setDescription("Foo One");

Copyright and all intellectual property belongs to Brockhaus Group 16

Page 17: Arquillian in a nutshell

Foo foo2 = new Foo(); foo2.setId(999l); foo2.setCreationTime(new Date(System.currentTimeMillis())); foo2.setDescription("Foo Two"); wrapper.getFoo().add(foo1); wrapper.getFoo().add(foo2); String json = JSONParser.getInstance().toJSON(wrapper); System.out.println(json); } private class FooWrapper { List<Foo> foo = new ArrayList<Foo>(); public List<Foo> getFoo() { return foo; } public void setFoo(List<Foo> foo) { this.foo = foo; } } }

Copyright and all intellectual property belongs to Brockhaus Group 17