Skip to content

Instantly share code, notes, and snippets.

@aslakknutsen
Last active September 20, 2016 09:12
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save aslakknutsen/3975179 to your computer and use it in GitHub Desktop.
Brute Force Arquillian Suite
<?xml version="1.0" encoding="UTF-8"?>
<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"/>
<extension qualifier="suite">
<property name="deploymentClass">org.jboss.arquillian.extension.suite.Deployments</property>
</extension>
</arquillian>
package org.jboss.arquillian.extension.suite;
import java.util.concurrent.Callable;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.container.spi.Container;
import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.container.spi.client.deployment.Deployment;
import org.jboss.arquillian.container.spi.client.deployment.DeploymentScenario;
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.arquillian.container.spi.event.DeployDeployment;
import org.jboss.arquillian.container.spi.event.DeployManagedDeployments;
import org.jboss.arquillian.container.spi.event.DeploymentEvent;
import org.jboss.arquillian.container.spi.event.UnDeployDeployment;
import org.jboss.arquillian.container.spi.event.UnDeployManagedDeployments;
import org.jboss.arquillian.container.spi.event.container.AfterStart;
import org.jboss.arquillian.container.spi.event.container.BeforeStop;
import org.jboss.arquillian.container.test.impl.client.deployment.event.GenerateDeployment;
import org.jboss.arquillian.core.api.Event;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.InstanceProducer;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.api.event.ManagerStarted;
import org.jboss.arquillian.core.spi.EventContext;
import org.jboss.arquillian.core.spi.LoadableExtension;
import org.jboss.arquillian.test.spi.TestClass;
import org.jboss.arquillian.test.spi.annotation.ClassScoped;
import org.jboss.arquillian.test.spi.annotation.TestScoped;
import org.jboss.arquillian.test.spi.context.ClassContext;
import org.jboss.arquillian.test.spi.event.suite.Before;
import org.jboss.arquillian.test.spi.event.suite.BeforeClass;
public class ArquillianSuiteExtension implements LoadableExtension {
public void register(ExtensionBuilder builder) {
builder.observer(SuiteDeployer.class);
}
public static class SuiteDeployer {
private Class<?> deploymentClass;
private DeploymentScenario suiteDeploymentScenario;
@Inject
@ClassScoped
private InstanceProducer<DeploymentScenario> classDeploymentScenario;
@Inject
private Event<DeploymentEvent> deploymentEvent;
@Inject
private Event<GenerateDeployment> generateDeploymentEvent;
@Inject
// Active some form of ClassContext around our deployments due to assumption bug in AS7 extension.
private Instance<ClassContext> classContext;
private ProtocolMetaData cachedProtocolMetaData;
@TestScoped
@Inject
private InstanceProducer<ProtocolMetaData> testScopedProtocolMetaData;
public void startup(@Observes(precedence = -100) ManagerStarted event, ArquillianDescriptor descriptor) {
deploymentClass = getDeploymentClass(descriptor);
executeInClassScope(new Callable<Void>() {
public Void call() throws Exception {
generateDeploymentEvent.fire(new GenerateDeployment(new TestClass(deploymentClass)));
suiteDeploymentScenario = classDeploymentScenario.get();
return null;
}
});
}
public void deploy(@Observes final AfterStart event, final ContainerRegistry registry) {
executeInClassScope(new Callable<Void>() {
public Void call() throws Exception {
for (Deployment d : suiteDeploymentScenario.deployments()) {
deploymentEvent.fire(new DeployDeployment(findContainer(registry,
event.getDeployableContainer()), d));
}
return null;
}
});
}
public void undeploy(@Observes final BeforeStop event, final ContainerRegistry registry) {
executeInClassScope(new Callable<Void>() {
public Void call() throws Exception {
for (Deployment d : suiteDeploymentScenario.deployments()) {
deploymentEvent.fire(new UnDeployDeployment(findContainer(registry,
event.getDeployableContainer()), d));
}
return null;
}
});
}
public void blockDeployManagedDeployments(@Observes EventContext<DeployManagedDeployments> ignored) {
// We need to block DeployManagedDeployments event
}
public void storeProtocolMetaData(@Observes ProtocolMetaData protocolMetaData) {
cachedProtocolMetaData = protocolMetaData;
}
public void resotreProtocolMetaData(@Observes EventContext<Before> eventContext) {
testScopedProtocolMetaData.set(cachedProtocolMetaData);
eventContext.proceed();
}
public void restoreDeploymentScenario(@Observes EventContext<BeforeClass> event) {
// Setup the Suite level scenario as if it came from the TestClass
event.proceed();
classDeploymentScenario.set(suiteDeploymentScenario);
}
public void blockUnDeployManagedDeployments(@Observes EventContext<UnDeployManagedDeployments> ignored) {
// We need to block UnDeployManagedDeployments event
}
private void executeInClassScope(Callable<Void> call) {
try {
classContext.get().activate(deploymentClass);
call.call();
} catch (Exception e) {
throw new RuntimeException("Could not invoke operation", e);
} finally {
classContext.get().deactivate();
}
}
private Container findContainer(ContainerRegistry registry, DeployableContainer<?> deployable) {
for (Container container : registry.getContainers()) {
if (container.getDeployableContainer() == deployable) {
return container;
}
}
return null;
}
private Class<?> getDeploymentClass(ArquillianDescriptor descriptor) {
if (descriptor == null) {
throw new IllegalArgumentException("Descriptor must be specified");
}
String className = descriptor.extension("suite").getExtensionProperties().get("deploymentClass");
if (className == null) {
throw new IllegalArgumentException(
"A extension element with property deploymentClass must be specified in arquillian.xml");
}
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load defined deploymentClass: " + className, e);
}
}
}
}
package org.jboss.arquillian.extension.suite;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.extension.suite.tests.Test1;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
public class Deployments {
@Deployment
public static WebArchive deploy() {
return ShrinkWrap.create(WebArchive.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addPackage(Test1.class.getPackage());
}
}
org.jboss.arquillian.extension.suite.ArquillianSuiteExtension
package org.jboss.arquillian.extension.suite.tests;
import javax.enterprise.inject.spi.BeanManager;
import junit.framework.Assert;
import org.jboss.arquillian.junit.Arquillian;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(Arquillian.class)
public class Test1 {
@Test
public void shouldInject(BeanManager bm) {
System.out.println("Where are We?");
Assert.assertNotNull(bm);
}
}
@aslakknutsen
Copy link
Author

The Extension will force all Classes in a Module into a TestSuite running from the same DeploymentScenario.

The deploymentClass defined in in the "suite" extension configuration will be used as a 'template' for all other TestClass scenarios.
TestClasses in the suite will have no BeforeClass or AfterClass events, as they are overridden by this extension to avoid new deployment scenarios being generated and new deployments being deployed.

Deployments.deploy() needs to manually include all TestClasses in the deployment that should be ran incontainer. Individual TestClasses/TestMethods in the suite are still allowed to defined @RunAsClient as normal.

@aslakknutsen
Copy link
Author

Call cycle:

  • ManagerStarted
    • GenerateDeployment (Deployments.deploy) (suite)
  • AfterStart (container)
    • DeployDeployments (all in suite DeploymentScenario)
  • BeforeClass (test)
    • Swap Test DeploymentScenario with Suite DeploymentScenario
    • no more events called in BeforeClass phase
  • AfterClass (test)
    • no more events called in AfterClass phase
  • BeforeStop (container)
    • UnDeployDeployments (all in suite DeploymentScenario)

@mojavelinux
Copy link

Perhaps stick this in the showcase for now?

@aslakknutsen
Copy link
Author

sure, the showcase is a good location

@rajn
Copy link

rajn commented Dec 14, 2012

Hi Aslak,

Is this a workaround for ARQ-567? If so can you please elaborate little bit on how to use this?

thank you,
raj

@jaygarala
Copy link

Hi Aslak,

Thanks for uploading this code. We have updated our framework to use this but we are running into issues with TestNG tests. The following example runs correctly when the class is not extending Arquillian(simple TestNG test class) and when is it extending Arquillian but ArquillianSuiteExtension is NOT used.

But when ArquillianSuiteExtension is used, testTwo() fails "expected [This is my updated message] but found [This is my message]".

I updated the example to print out the test instance. Without the ArquillianSuiteExtension, both tests print out is the same. But when ArquillianSuiteExtension is used, they are different.

import org.jboss.arquillian.testng.Arquillian;

import org.testng.Assert;
import org.testng.annotations.Test;

public class SimpleTestNGTest extends Arquillian
{
    private String message= "This is my message";

    @Test
    public void testOne()
    {
        System.out.println(this);
        Assert.assertEquals( "This is my message", message );
        message = "This is my updated message";
    }

    @Test(dependsOnMethods = "testOne")
    public void testTwo()
    {
        System.out.println(this);
        Assert.assertNotNull( message );
        Assert.assertEquals( message, "This is my updated message" );
    }
}

@aslakknutsen
Copy link
Author

@jaygarala this is not really related to the test suite or not. Arquillian will reexecute the whole test framework lifecycle incontainer for each @test on the client side.

@Before
@Test
    -> @Before (in container)
    -> @Test (in container)
    -> @After (in container)
@After

As long as you don't define a @deployment method, the execution is not moved to the container, and 'everything' works as normal TestNG. By adding the ArquillianSuiteExtension, you're probably adding a Deployment (via the Deployments class), which then moves the default behavior to be incontainer execution.

@jaygarala
Copy link

Hi Aslak,

Thank you for responding. The test case I posted is to simplify the issue we are experiencing. Here is a more realistic in-container test.

The createUser() will created the User and verifyUser() will assert user is non null. In each test it will print out the object instance to sout.

import org.jboss.arquillian.testng.Arquillian;
import org.testng.Assert;

import org.testng.annotations.Test;

public class SimpleTestNGTest extends Arquillian
{
    private User user;

    @Test
    public void createUser()
    {
        user = createUserInTheDb();
        System.out.println( this );
    }

    @Test(dependsOnMethods = "createUser")
    public void verifyUser()
    {
        System.out.println(this);
        Assert.assertNotNull( user );
    }

    private User createUserInTheDb()
    {
        // pseudo create user
        return new User( "123", "qwe" );
    }

    class User
    {
        private String userName, password;

        User( String userName, String password )
        {
            this.userName = userName;
            this.password = password;
        }
    }
}

When the test runs in container, here is the test output.

SimpleTestNGTest@7fa0898d
SimpleTestNGTest@2e68f296
java.lang.AssertionError: expected object to not be null

Note the instances are different. Under regular TestNG test and non-suite Arquillian test they are the same. Wondering what is causing the class instances to be different while in suite Arquillian test.

@rajn
Copy link

rajn commented Dec 18, 2012

Hi Aslak,

Have you put this in the showcase area as has been suggested? - That way I think I can understand a little bit better. Right now I'm able to make the suite work but when I use it along with the Seam2 extension it runs into trouble.

Thanks in advance,
raj

@aslakknutsen
Copy link
Author

@rajin there was a bug in the seam2 extension that caused some issues, this should be fixed in 1.0.0.Final-SNAPSHOT. Try fetching it from the JBoss nexus repo: https://repository.jboss.org/nexus/content/groups/public/

@rajn
Copy link

rajn commented Dec 19, 2012

Hi Aslak,

Super! - that was it.

Thanks a bunch
raj

@carlosmunoz
Copy link

Hi Aslak,

I'm trying to use this extension along with the Seam2 extension but I seem to be running into trouble when starting the test. I am getting the following exception:

java.lang.IllegalArgumentException: No active SuiteScoped Context to bind to
at org.jboss.arquillian.core.impl.ManagerImpl.bind(ManagerImpl.java:173)
at org.jboss.arquillian.core.impl.ManagerImpl.bindAndFire(ManagerImpl.java:235)
at org.jboss.arquillian.core.impl.InstanceImpl.set(InstanceImpl.java:74)
at org.jboss.as.arquillian.protocol.jmx.JMXProtocolAS7.getPackager(JMXProtocolAS7.java:45)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.buildTestableDeployments(DeploymentGenerator.java:169)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.createTestableDeployments(DeploymentGenerator.java:148)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.generateDeployment(DeploymentGenerator.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)
at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
at org.jboss.arquillian.core.impl.EventImpl.fire(EventImpl.java:67)
at org.drools.guvnor.arquilliansuite.ArquillianSuiteExtension$SuiteDeployer$1.call(ArquillianSuiteExtension.java:55)
at org.drools.guvnor.arquilliansuite.ArquillianSuiteExtension$SuiteDeployer$1.call(ArquillianSuiteExtension.java:53)
at org.drools.guvnor.arquilliansuite.ArquillianSuiteExtension$SuiteDeployer.executeInClassScope(ArquillianSuiteExtension.java:95)

My test is a TestNG test, but I've also tried with the JUnit runner and get the same results. Any ideas on what may be going wrong here?

@MaikelNait
Copy link

Silly question : How can I get this extension running for my Arquillian tests ? How to compile the classes , and where to put them ?

I'm pretty new to Arquillian , and I can't find good guides about how to use Extensions ( other than the Persistence extension which is available as a maven dependency ).

What is the main difference between this extension and the JRebel one ?
Are they targeted to fulfill the same goal ? ( JRebel extension seems already available as a jar )

Thanks !

@blabno
Copy link

blabno commented Apr 12, 2013

MaikeInait: JRebel extension is for development time (i.e. you should not use it on Jenkins). Suite extension is meant to run tests from several classes in single deployment (i.e. Jenkins could do 1 deployment with all tests instead of 100, not mentioning server start/stop)

Aslak: Does this run with Drone?

@blabno
Copy link

blabno commented Apr 15, 2013

I've got it working with Drone! Check out my fork: https://gist.github.com/blabno/5387599

@blabno
Copy link

blabno commented Apr 25, 2013

@lynchmaniac
Copy link

Like MaikelNait i'm really newbie on Arquillian and i don't see how to launch all the test. I made a project with all the class, compilation is OK but in my Eclipse i didn't see how to launch all the test. With maven i have several deployements, one per test. So i do something wrong i think :-)
Is anyone can help me on this problem ?

@MaikelNait
Copy link

Thanks for the clarification blabno... the only thing I'm still missing is how to integrate this Suite plugin into my Arquillian configuration. I just need to copy all the source code into my project, and make it compile ?
Where to put the "LoadableExtension" file that refers to org.jboss.arquillian.extension.suite.ArquillianSuiteExtension ?
Thanks a lot in advance !

@MaikelNait
Copy link

Mmm... searching in Internet , looks like I may need to re-compile the whole Arquillian project , to get the Suite Extension registered in a file called /META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension ?!?!?

I can see this file inside arquillian-container-impl-base-1.0.3.Final.jar , and arquillian-config-impl-base-1.0.3.Final.jar,
with just one line : org.jboss.arquillian.container.impl.ContainerExtension

Can anyone just post a good documentation on how to get this Suit integrated into JBoss "for dummies" ???
Thanks a lot !!

@MaikelNait
Copy link

Ok, seems I just need to rename the "LoadableExtension" file as "org.jboss.arquillian.core.spi.LoadableExtension" , and place it under /src/main/resources/META-INF/services ( I'm using a Maven project )... Now the Suite if finally working ...!!! A good installation guide for this Extension would have been very nice .. but anyway, I finally figured it out ... phew !

@javaG
Copy link

javaG commented May 26, 2013

Hi Aslak,
I've managed to use the brute force suite solution, thank You for your well-done job. Everything works fine while one issue still exists: I see that when using the suite, arquillian deployment algorithm tries to instantiate ALL the singleton classes found on a classpath (WAR provided by the ShrinkWrap as part of @deployment). We have a large-scale project and classpath I'm trying to use is pretty big, but I'm not expecting this kind of eager loading during deployment.
My question is: is it supposed to work this way, or it's just a bug? BTW, when running arquillian tests without suite-extension, I don't see this problem at all, seems like lazy-loading model implemented then, as it supposed to be.
Thanks for your answer in advance.

@asceta
Copy link

asceta commented Jul 19, 2013

Welcome,

I am also using DBUnit extension configured as

    <extension qualifier="persistence-dbunit">
        <property name="datatypeFactory">org.dbunit.ext.h2.H2DataTypeFactory</property>
        <property name="excludePoi">true</property>
        <property name="caseSensitiveTableNames">false</property>
    </extension>

and I am facing problems during test execution:

Caused by: java.lang.NullPointerException
    at org.jboss.arquillian.persistence.core.deployment.PersistenceExtensionArchiveAppender.requiredLibraries(PersistenceExtensionArchiveAppender.java:81)
    at org.jboss.arquillian.persistence.core.deployment.PersistenceExtensionArchiveAppender.createAuxiliaryArchive(PersistenceExtensionArchiveAppender.java:58)
    at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.loadAuxiliaryArchives(DeploymentGenerator.java:209)
    at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.buildTestableDeployments(DeploymentGenerator.java:160)
    at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.createTestableDeployments(DeploymentGenerator.java:148)
    at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.generateDeployment(DeploymentGenerator.java:85)

...

at org.jboss.arquillian.extension.suite.ArquillianSuiteExtension$SuiteDeployer.executeInClassScope

Could you please help me how should I configure my env properly ?

@ingwarsw
Copy link

I just created my fork out of ITCrowd one.

I added extra Annotation for selecting global deployment.

https://github.com/ingwarsw/arquillian-suite-extension

@suikast42
Copy link

Hi Aslak,
if I run this extension with a single test ( for example mvn -Dtest=ATest test) then ARQ undeploy my app automatically. But if I run the suite with mvn test on my project everything works well but ARQ doesn't undeploy my app.

My environment:
ARQ version : 1.1.2.Final
App server: Jboss 7.2.0.Final

Did I miss something with the configuration?

@KrishnaKotari
Copy link

Hello Aslak,

I'm getting the following error while using this extension. I am using TestNG with Arrquillian

Caused by: java.lang.IllegalArgumentException: No active SuiteScoped Context to bind to
at org.jboss.arquillian.core.impl.ManagerImpl.bind(ManagerImpl.java:173)
at org.jboss.arquillian.core.impl.ManagerImpl.bindAndFire(ManagerImpl.java:235)
at org.jboss.arquillian.core.impl.InstanceImpl.set(InstanceImpl.java:74)
at org.jboss.as.arquillian.protocol.jmx.JMXProtocolAS7.getPackager(JMXProtocolAS7.java:45)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.buildTestableDeployments(DeploymentGenerator.java:169)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.createTestableDeployments(DeploymentGenerator.java:148)
at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.generateDeployment(DeploymentGenerator.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

Can you please help, what is it that I am missing. Configured the extension as shown above.

@KrishnaKotari
Copy link

Hello Asalak,

I have injected the suiteContext at the class level

@Inject
private Instance suiteContext;

and activated the contect in the method startup
suiteContext.activate();

With this it started working like a charm.

Thanks,
Krishna.

@perabello
Copy link

Hello Asceta,

I use Persistence and DBUnit extension too (defaultDataSource, defaultDataSeedStrategy, datatypeFactory, defaultDataSetLocation, etc) .
I do not find how to configure it integrated with ArquillianSuiteExtension.
Somebody is using it?

Thanks
Pere

@Steve973
Copy link

Steve973 commented Apr 6, 2016

Hello Aslak. Does this have to use a war? I want to run tests in Karaf, so I need to deploy bundles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment