結合テスト

Arquillian

アーキテクチャ

★結合テストを実行する前の注意点

1.$JBOSS_HOME/standalone/deployments フォルダに何もないこと

2.standalone.xmlに<deployments>を削除すること

3.実行例

clean install -Pci -pl services-impl pre-integration-test -DskipTests=true

★コンテナとの連携方法

・remote 既に起動しているコンテナに対してテストを行う

・managed 指定されたコンテナを利用してテストを行う(コンテナの起動と停止は自動的に)

・embedded 組込みコンテナを利用して全てがJVM上で行う

※remoteやmanaged 環境の再現性が高く、インテグレーションテスト向け

※embedded 環境の再現性が低く、単体テスト向け

profile設定(JBossASの場合)

<profile>

<id>arq-jbossas-managed</id>

<dependencies>

<dependency>

<groupId>org.jboss.as</groupId>

<artifactId>jboss-as-arquillian-container-managed</artifactId>

<version>7.1.1.Final</version>

<scope>test</scope>

</dependency>

</dependencies>

</profile>

<profile>

<id>arq-jbossas-remote</id>

<dependencies>

<dependency>

<groupId>org.jboss.as</groupId>

<artifactId>jboss-as-arquillian-container-remote</artifactId>

<version>7.1.1.Final</version>

<scope>test</scope>

</dependency>

</dependencies>

</profile>

remote実行

mvn -P arq-jbossas-remote test

※事前にJBoss ASを起動

managed実行

$JBOSS_HOME=/opt/jboss-eap-6.1 mvn -P arq-jbossas-managed test

embedded実行(GlassFishの場合)

<dependency>

<groupId>org.jboss.arquillian.container</groupId>

<artifactId>arquillian-glassfish-embedded-3.1</artifactId>

<version>1.0.0.CR3</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.glassfish.main.extras</groupId>

<artifactId>glassfish-embedded-web</artifactId>

<version>3.1.2.2</version>

<scope>test</scope>

</dependency>

★単体Frameworkとの連携

TestNGの場合

<dependency>

<groupId>org.jboss.arquillian.testng</groupId>

<artifactId>arquillian-testng-container</artifactId>

<version>1.0.0.CR1</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.testng</groupId>

<artifactId>testng</artifactId>

<version>6.4</version>

<scope>test</scope>

</dependency>

JUnitの場合

<dependency>

<groupId>org.jboss.arquillian.junit</groupId>

<artifactId>arquillian-junit-container</artifactId>

<version>1.0.0.CR1</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

<scope>test</scope>

</dependency>

//JUnit

import org.junit.runner.RunWith;

import org.jboss.arquillian.junit.Arquillian;

//TestNG

import org.jboss.arquillian.testng.Arquillian;

import org.jboss.arquillian.api.Deployment;

import org.jboss.arquillian.junit.Arquillian;

import org.jboss.shrinkwrap.api.Archives;

import org.jboss.shrinkwrap.api.ArchivePaths;

import org.jboss.shrinkwrap.api.spec.JavaArchive;

import org.jboss.shrinkwrap.impl.base.asset.ByteArrayAsset;

//JUnit

@RunWith(Arquillian.class)

public class XxxIT {

//TestNG

public class XxxIT extends Arquillian {

★設定ファイル:arquillian.xml

<arquillian ...>

<defaultProtocol type="Servlet 3.0" />

<container qualifier="glassfish-embedded" default="true">

<configuration>

<property name="resourcesXml">

src/test/resources/glassfish-resources.xml

</property>

</configuration>

</container>

<container qualifier="jboss-local" default="false">

<configuration>

<property name="javaVmArguments">

-Djboss.socket.binding.port-offset=${jboss-as.port.offset}

-Xmx512m -XX:MaxPermSize=360m

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787

</property>

<property name="managementPort">

${jboss-as.management.port}

</property>

<property name="outputToConsole">true</property>

</configuration>

</container>

<container qualifier="jboss-ci" default="false">

<configuration>

<property name="javaVmArguments">

-Djboss.socket.binding.port-offset=${jboss-as.port.offset}

-Xmx512m -XX:MaxPermSize=360m

</property>

<property name="jbossHome">${jboss-as.home}</property>

<property name="managementPort">

${jboss-as.management.port}

</property>

<property name="outputToConsole">true</property>

</configuration>

</container>

</arquillian>

※デバッグ手順:(青字を追加)

Step1 Debug As → TestNG Test / JUnit Test

Step2 Remote Java Application

glassfish-resources.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE resources PUBLIC

"-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"

"http://glassfish.org/dtds/glassfish-resources_1_5.dtd">

<resources>

<jdbc-resource pool-name="H2Pool" jndi-name="jdbc/xxx" />

<jdbc-connection-pool name="H2Pool"

res-type="javax.sql.XADataSource"

datasource-classname="org.h2.jdbcx.JdbcDataSource">

<property name="url" value="jdbc:h2:mem:;DB_CLOSE_DELAY=-1" />

</jdbc-connection-pool>

</resources>

★Mode

In-container mode(By default)

@Stateless

public class UserServiceBean {

@PersistenceContext

private EntityManager em;

public User addUser(User user) {

em.persist(user);

return user;

}

// Transactionなし

@TransactionAttribute(TransactionAttributeType.SUPPORTS)

public User findById(Long id) {

return em.find(User.class, id);

}

}

public class UserServiceBeanIT extends Arquillian {

private static final Logger LOGGER

= Logger.getLogger(UserServiceBeanIT.class.getName());

@Inject

private UserServiceBean service;

@Deployment

public static JavaArchive createTestArchive() {

final JavaArchive jar

= ShrinkWrap.create(JavaArchive.class, "test.jar")

.addClasses(User.class, UserServiceBean.class)

.addAsManifestResource(

"META-INF/persistence.xml", "persistence.xml")

.addAsManifestResource(

EmptyAsset.INSTANCE,

ArchivePaths.create("beans.xml"));

LOGGER.info(jar.toString(Formatters.VERBOSE));

return jar;

}

@Test

public void shouldXxx() {

final User user = new User("Andy");

service.addUser(user);

assertNotNull(user.getId());

}

}

Client mode

@WebServlet("/user")

public class UserServlet extends HttpServlet {

@Inject

private UserServiceBean service;

@Override

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

...

User user = service.addUser(new User("Andy");

...

}

...

}

public class UserServletIT extends Arquillian {

private static final Logger LOGGER

= Logger.getLogger(UserServletIT.class.getName());

@Deployment(testable = false)

public static WebArchive createTestArchive() {

final WebArchive war

= ShrinkWrap.create(WebArchive.class, "test.war")

.addClasses(

User.class, UserServiceBean.class, UserServlet.class)

.addAsResource("META-INF/persistence.xml")

.addAsWebInfResource(

EmptyAsset.INSTANCE,

ArchivePaths.create("beans.xml"));

LOGGER.info(war.toString(Formatters.VERBOSE));

return war;

}

@RunAsClient

@Test(dataProvider = Arquillian.ARQUILLIAN_DATA_PROVIDER)

public void shouldXxx(@ArquillianResource URL baseURL) throws IOException {

final URL url = new URL(baseURL, "user"); //<context_path>/user

final User user = new User(1L, "Andy");

StringBuilder sb = new StringBuilder();

BufferedReader reader

= new BufferedReader(new InputStreamReader(url.openStream()));

String line;

while ((line = reader.readLine()) != null) {

sb.append(line);

}

reader.close();

assertEquals(user.toString(), sb.toString());

}

}

Mixed mode

★HtmlUnit

<dependency>

<groupId>net.sourceforge.htmlunit</groupId>

<artifactId>htmlunit</artifactId>

<version>2.8</version>

<scope>test</scope>

</dependency>

import com.gargoylesoftware.htmlunit.WebClient;

import com.gargoylesoftware.htmlunit.html.HtmlPage;

import org.jboss.arquillian.container.test.api.Deployment;

import org.jboss.arquillian.junit.Arquillian;

import org.jboss.arquillian.test.api.ArquillianResource;

import org.jboss.shrinkwrap.api.Filters;

import org.jboss.shrinkwrap.api.GenericArchive;

import org.jboss.shrinkwrap.api.ShrinkWrap;

import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;

import org.jboss.shrinkwrap.api.importer.ExplodedImporter;

import org.jboss.shrinkwrap.api.spec.WebArchive;

import org.jboss.shrinkwrap.resolver.api.maven.Maven;

import org.junit.runner.RunWith;

@RunWith(Arquillian.class)

public class ArquillianHtmlUnitTest {

@Deployment

public static WebArchive createTestArchive() {

Collection<String> dependencies = Arrays.asList(new String[]{

"javax.servlet:jstl",

"taglibs:standard",

"commons-lang:commons-lang"

});

File[] libs = Maven.resolver()

.loadPomFromFile("pom.xml").resolve(dependencies)

.withTransitivity().asFile();

WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")

// /WEB-INF/classes/*

.addClasses(MyManager.class, ActionServlet.class)

// /WEB-INF/classes/META-INF/*

.addAsResource(new ClassLoaderAsset("META-INF/ejb-jar.xml"),

"META-INF/ejb-jar.xml")

.addAsResource(new ClassLoaderAsset(

"META-INF/persistence.xml"),"META-INF/persistence.xml")

.addAsLibraries(libs); // /WEB-INF/lib/*

war.merge(ShrinkWrap.create(GenericArchive.class)

.as(ExplodedImporter.class)

.importDirectory("src/main/webapp")

.as(GenericArchive.class), "/", Filters.includeAll());

return war;

}

@ArquillianResource

private URL deploymentUrl; //http://localhost:xxx/test/

@Before

@After

public void clean() {

// DBのクリーン処理など

}

@Test

public void shouldXxx() throws Exception {

WebClient webClient = new WebClient();

//JSP例

HtmlPage page = webClient.getPage(deploymentUrl + "/xxx.jsp");

assertPresent(page);

//Servlet例

page = webClient.getPage(deploymentUrl + "/yyy");

assertPresent(page);

webClient.closeAllWindows();

}

private void assertPresent(HtmlPage page) {

String pageAsText = page.asText();

assertTrue(pageAsText.contains("..."));

}

}

★Testing EJB

方法1 Arquillian

@RunWith(Arquillian.class)

public class EjbIT {

@Deployment

public static JavaArchive createTestArchive() {

return Archives.create("test.jar", JavaArchive.class)

.addClasses(MyManager.class, MyManagerBean.class);

}

//あるいは

@Deployment

public static WebArchive createTestArchive() {

return ShrinkWrap.create(WebArchive.class, "test.war")

.addClasses(MyManager.class, MyManagerBean.class)

.addAsResource(new ClassLoaderAsset("META-INF/ejb-jar.xml"), "META-INF/ejb-jar.xml")

.addAsResource(new ClassLoaderAsset("META-INF/persistence.xml"), "META-INF/persistence.xml");

}

@EJB

private MyManager myManager;

@Test

public void shouldXxx() throws Exception {

assertEquals("...", myManager.method("xxx"));

}

}

方法2 EmbeddedEJB

import javax.ejb.embeddable.EJBContainer;

public class EjbIT {

private static EJBContainer ejbContainer;

@BeforeClass

public static void setUp() throws Exception {

ejbContainer = EJBContainer.createEJBContainer();

}

@AfterClass

public static void tearDown() {

if (ejbContainer != null) {

ejbContainer.close();

}

}

@Test

public void shouldXxx() throws Exception {

MyManagerBean bean =

(MyManagerBean) ejbContainer.getContext().lookup(

"java:global/<project>/MyManagerBean!<package>.MyManagerBean");

...

}

}

★Testing CDI

import javax.enterprise.inject.spi.BeanManager;

import javax.inject.Inject;

@RunWith(Arquillian.class)

public class CdiIT {

@Deployment

public static JavaArchive createTestArchive() {

return Archives.create("test.jar", JavaArchive.class)

.addClasses(MyManager.class, MyManagerBean.class)

.addManifestResource(new ByteArrayAsset(new byte[0])),

ArchivePaths.create("beans.xml"));

}

@Inject

private MyManager myManager;

@Inject

private BeanManager beanManager;

@Test

public void shouldXxx() throws Exception {

assertNotNull(beanManager);

assertEquals("...", myManager.method("xxx"));

}

}

★Testing JPA

@Stateless

public class UserRepositoryBean implements UserRepository {

@PersistenceContext

private EntityManager em;

public void addUser(User u) {

em.persist(u);

em.flush();

}

public List<User> findByName(String name) {

return em.createQuery("SELECT u FROM User u WHERE u.name = :name")

.setParameter("name", name)

.getResultList();

}

}

@RunWith(Arquillian.class)

public class UserRepositoryIT {

private static final String NAME = "Andy";

@Deployment

public static JavaArchive createTestArchive() {

return Archives.create("test.jar", JavaArchive.class)

.addClasses(User.class, UserRepository.class, UserRepositoryBean.class)

.addManifestResource(

"test-persistence.xml", ArchivePaths.create("persistence.xml"));

}

@EJB

private UserRepository userRepository;

@Test

public void shouldXxx() {

User u = new User(NAME);

userRepository.addUser(u);

List<User> users = userRepository.findByName(NAME);

assertNotNull(users);

assertTrue(users.size() == 1);

assertEquals(users.get(0).getName(), NAME);

}

}

★Testing JMS

import javax.annotation.Resource;

import javax.jms.*;

@RunWith(Arquillian.class)

public class JmsIT {

@Deployment

public static JavaArchive createTestArchive() {

return Archives.create("test.jar", JavaArchive.class)

.addClasses(MyMessageEcho.class, MyQueueRequestor.class);

}

@Resource(mappedName="/queue")

private Queue queue;

@Resource(mappedName="/ConnectionFactory")

private ConnectionFactory factory;

@Test

public void shouldXxx() throws Exception {

String messageBody = "xxx";

Connection connection = factory.createConnection();

Session session

= connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

MyQueueRequestor requestor

= new MyQueueRequestor((QueueSession) session, queue);

connection.start();

Message request = session.createTextMessage(messageBody);

Message response = requestor.request(request, 5000);

assertEquals(messageBody, ((TextMessage) response).getText());

}

}

★Testing JAX-RS

@RequestScoped

@Path("calc")

public class Calc {

@Inject

private CalcBean calcBean;

@GET

@Path("add")

public int add(@QueryParam("x") int x, @QueryParam("y") int y) {

return calcBean.add(x, y);

}

}

@Stateless

@LocalBean

public class CalcBean {

public int add(int x, int y) {

return x + y;

}

}

import static org.hamcrest.CoreMatchers.*;

import static org.junit.Assert.*;

import java.nio.file.Path;

import java.nio.file.Paths;

import org.jboss.arquillian.container.test.api.Deployment;

import org.jboss.arquillian.container.test.api.RunAsClient;

import org.jboss.arquillian.junit.Arquillian;

import org.jboss.shrinkwrap.api.ShrinkWrap;

import org.jboss.shrinkwrap.api.spec.WebArchive;

import org.junit.Test;

import org.junit.runner.RunWith;

import com.sun.jersey.api.client.Client;

@RunWith(Arquillian.class)

public class CalcTest {

@Deployment

public static WebArchive createDeployment() {

Path webinf = Paths.get("src", "main", "webapp", "WEB-INF");

return ShrinkWrap

.create(WebArchive.class, "test.war")

.addClasses(Calc.class, CalcBean.class, CalcApplication.class)

.addAsWebInfResource(webinf.resolve("beans.xml").toFile());

}

@Inject

private Calc calc;

@Test

public void addUT() throws Exception {

int result = calc.add(1, 2);

assertThat(result, is(3));

}

@Test

@RunAsClient

public void addIT() throws Exception {

String response =

Client

.create()

.resource("http://localhost:8080/xxx/calc/add")

.queryParam("x", "1")

.queryParam("y", "2")

.get(String.class);

assertThat(response, is("3"));

}

}

★ShrinkWrap (Archives)

import static org.apache.ziplock.JarLocation.jarLocation;

import static org.jboss.shrinkwrap.resolver.api.maven.Maven.resolver;

import org.jboss.arquillian.container.test.api.Deployment;

import org.jboss.shrinkwrap.api.Archive;

import org.jboss.shrinkwrap.api.ArchivePaths;

import org.jboss.shrinkwrap.api.Node;

import org.jboss.shrinkwrap.api.Filters;

import org.jboss.shrinkwrap.api.ShrinkWrap;

import org.jboss.shrinkwrap.api.asset.EmptyAsset;

import org.jboss.shrinkwrap.api.asset.StringAsset;

import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;

import org.jboss.shrinkwrap.api.spec.JavaArchive;

import org.jboss.shrinkwrap.api.spec.WebArchive;

import org.jboss.shrinkwrap.api.exporter.ZipExporter;

public class Deployments {

@Deployment

public static Archive<?> deployment() {

Archive<?> archive = ShrinkWrap

.create(EnterpriseArchive.class, "test.ear")

.addAsModule(createWar())

.addAsLibrary(createJar())

.addAsApplicationResource(

"META-INF/jboss-deployment-structure.xml",

"jboss-deployment-structure.xml");

System.out.println(archive.toString(true));

return archive;

}

private static Archive<?> createWar() {

Archive<?> archive = ShrinkWrap

.create(WebArchive.class, "test.war")

.addAsResource("META-INF/persistence.xml")

.addAsDirectory("WEB-INF/classes")

.addAsWebInfResource("META-INF/beans.xml", "beans.xml")

.addAsWebInfResource("META-INF/testPersistence.xml",

"classes/META-INF/persistence.xml")

.addAsWebInfResource(

new File("src/test/resources", "glassfish-web.xml"))

.addAsWebInfResource(

new File("src/main/webapp", "WEB-INF/beans.xml"))

.addAsLibraries(createJar())

.addAsLibraries(jarLocation(MyBean.class))

.addAsLibraries(

resolver().resolve("groupId:artifactId:version")

.withTransitivity().asFile());

System.out.println(archive.toString(true));

return archive;

}

private static Archive<?> createJar() {

Archive<?> archive = ShrinkWrap

.create(JavaArchive.class, "test.jar")

.addClass(MyBean.class)

.addClasses(MyBeanA.class, MyBeanB.class)

.addPackage("com.xxx")

.addPackage(MyBean.class.getPackage())

.addPackages(true, "com.xxx", "com.yyy")

.addAsResource("META-INF/beans.xml", "beans.xml")

.addAsResource(EmptyAsset.INSTANCE, "META-INF/beans.xml")

.addAsResource(

"META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension")

.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")

.addAsManifestResource(EmptyAsset.INSTANCE,

ArchivePaths.create("beans.xml"))

.addAsManifestResource(

new StringAsset(

"<decorators><class>com.test.MyDecorator<class></decorators>"),

"beans.xml");

System.out.println(archive.toString(true));

return archive;

}

private static void export(Archive<?> archive, String path) {

archive.as(ZipExporter.class).exportTo(new File(path), true);

}


private static void deleteJar(Archive<?> archive) {

String regexp = "^/WEB-INF/lib/XXX.*\\.jar$";

Map<ArchivePath, Node> content = archive.getContent(Filters.include(regexp));


for (ArchivePath ap : content.keySet()) {

archive.delete(ap.get());

}


archive.delete("/WEB-INF/lib/XXX.jar");

}

}

★ShrinkWrap Resolvers (Maven, Gradle)

<dependency>

<groupId>org.jboss.shrinkwrap.resolver</groupId>

<artifactId>shrinkwrap-resolver-depchain</artifactId>

<version>${shrinkwrap.resolvers.version}</version>

<scope>test</scope>

<type>pom</type>

</dependency>

@Deployment

public static Archive<?> createTestArchive() {

// Usage: GroupId:ArtifactId:[Packaging:Classifier:]Version

String[] deps = {

"org.apache.commons:commons-lang3:3.1",

"org.apache.commons:commons-lang3:jar:sources:3.1"

};

File[] libs = Maven.configureResolver()

.fromClassloaderResource("custom-settings.xml")

.resolve(deps)

.withMavenCentralRepo(false)

.withoutTransitivity()

.asFile();

File[] libs = Maven.resolver()

.resolve(deps)

.withTransitivity()

.asFile();

File[] libs = Maven.resolver()

.loadPomFromFile("pom.xml")

.resolve("GroupId:ArtifactId")

.withTransitivity()

.asFile(); // as(File.class)

File[] libs = Maven.resolver()

.loadPomFromFile("pom.xml")

.importRuntimeDependencies()

.resolve()

.withTransitivity()

.asFile();

File[] libs = Maven.resolver()

.loadPomFromFile("pom.xml")

.importCompileAndRuntimeDependencies()

.resolve()

.withoutTransitivity()

.asFile();

File lib = Maven.resolver()

.resolve("GroupId:ArtifactId:Version")

.withoutTransitivity()

.asSingleFile(); // asSingle(File.class)

return ShrinkWrap.create(WebArchive.class, "test.war")

.addClasses(MyBean.class)

.addAsResource(

"META-INF/test-persistence.xml", "META-INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")

.addAsLibraries(libs)

.addAsModule(lib, "test.jar")

.addAsWebInfResource("test-ds.xml");

}

WebArchive webArchive = ShrinkWrap.create(MavenImporter.class)

.loadPomFromFile("pom.xml")

.importBuildOutput()

.as(WebArchive.class);

WebArchive webArchive = ShrinkWrap.create(EmbeddedGradleImporter.class)

.forProjectDirectory(dir)

.importBuildOutput()

.as(WebArchive.class);