14 Replies Latest reply on May 6, 2013 10:50 AM by lynchmaniac

    Single @Deployment used across multiple TestNG test classes

    magnus.smith

      Hello,  I'm pretty new to Arquillian and would be very appreciative of any advice that anyone might be able to offer with a problem that I'm encountering.

       

      I'm using Arquillian (version 1.0.0.Alpha4) and testNG(version 5.14) to test EJBs with EclipseLink using Embedded Glassfish.

       

      Everything works fine for me if I have just a single testcase class that has the @Deployment anntotation to create the archive and I do all my tests in that class.

       

      What I would like to do is

       

      1. have a single testcase class in a group called 'setup' that does the ejb deployment and creates the db tables.
      2. have other testcase classes that are in groups that depend upon 'setup' group so that I can test each of the EJBs in turn with only doing the single deployment.

       

      The problem that I am hitting is that arquillian seems to expect there to be a @Deployment annotation in each of the test classes.

       

      The following stacktrace is generated when I run the first testcase that depends upon the 'setup' group

       

      org.jboss.arquillian.impl.event.FiredEventException: java.lang.IllegalArgumentException: No method annotated with org.jboss.arquillian.api.Deployment found
              at org.jboss.arquillian.impl.event.MapEventManager.fire(MapEventManager.java:68)
              at org.jboss.arquillian.impl.context.AbstractEventContext.fire(AbstractEventContext.java:115)
              at org.jboss.arquillian.impl.EventTestRunnerAdaptor.beforeClass(EventTestRunnerAdaptor.java:96)
              at org.jboss.arquillian.testng.Arquillian.arquillianBeforeClass(Arquillian.java:77)
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
              at java.lang.reflect.Method.invoke(Method.java:597)
              at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:640)
              at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:503)
              at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:193)
              at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:123)
              at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:183)
              at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:115)
              at org.testng.TestRunner.runWorkers(TestRunner.java:1098)
              at org.testng.TestRunner.privateRun(TestRunner.java:727)
              at org.testng.TestRunner.run(TestRunner.java:581)
              at org.testng.SuiteRunner.runTest(SuiteRunner.java:315)
              at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:310)
              at org.testng.SuiteRunner.privateRun(SuiteRunner.java:272)
              at org.testng.SuiteRunner.run(SuiteRunner.java:221)
              at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:40)
              at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:83)
              at org.testng.internal.thread.ThreadUtil$CountDownLatchedRunnable.run(ThreadUtil.java:151)
              at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
              at java.lang.Thread.run(Thread.java:619)
      Caused by: java.lang.IllegalArgumentException: No method annotated with org.jboss.arquillian.api.Deployment found
              at org.jboss.arquillian.impl.DeploymentAnnotationArchiveGenerator.generateApplicationArchive(DeploymentAnnotationArchiveGenerator.java:45)
              at org.jboss.arquillian.impl.ClientDeploymentGenerator.generate(ClientDeploymentGenerator.java:59)
              at org.jboss.arquillian.impl.handler.ArchiveGenerator.callback(ArchiveGenerator.java:52)
              at org.jboss.arquillian.impl.handler.ArchiveGenerator.callback(ArchiveGenerator.java:42)
              at org.jboss.arquillian.impl.event.MapEventManager.fire(MapEventManager.java:63)
              ... 26 more
      Caused by: java.lang.IllegalArgumentException: No method annotated with org.jboss.arquillian.api.Deployment found
              at org.jboss.arquillian.impl.DeploymentAnnotationArchiveGenerator.generateApplicationArchive(DeploymentAnnotationArchiveGenerator.java:45)
              at org.jboss.arquillian.impl.ClientDeploymentGenerator.generate(ClientDeploymentGenerator.java:59)
              at org.jboss.arquillian.impl.handler.ArchiveGenerator.callback(ArchiveGenerator.java:52)
              at org.jboss.arquillian.impl.handler.ArchiveGenerator.callback(ArchiveGenerator.java:42)
              at org.jboss.arquillian.impl.event.MapEventManager.fire(MapEventManager.java:63)
              ... 26 more

      thanks

        • 1. Re: Single @Deployment used across multiple TestNG test classes
          aslak

          Multiple TestCases against the same Deployment is not supported in Arquillian 1.0.0.Alpha4. This is a planned feature tho, so it will be in by the 1.0 release.

          1 of 1 people found this helpful
          • 2. Re: Single @Deployment used across multiple TestNG test classes
            magnus.smith

            This will be a really welcome addition

            • 3. Re: Single @Deployment used across multiple TestNG test classes
              jadrake

              Yes - I agree.  In my situation I would like to test a series of services dealing with various JPA beans and it would be nice to be able to create a single Deployment and then execute those tests in that deployment.

               

              -Jason

              • 4. Re: Single @Deployment used across multiple TestNG test classes
                shogatsu

                Hi, isn't it possible to extend an abstract class in which you put your @deployment ? By extending this class each of your test class would be runnable by arquillian

                • 5. Re: Single @Deployment used across multiple TestNG test classes
                  aslak

                  That is possible, but you have to manually add the Abstract class ot the deployment.

                   

                  But the question here is, you want to split up into different test cases that test different areas of a single deployment. You won't have to redefine the deployment it self if you use a Abstract class or a static factory or similar, but the deployment will be redeployed pr test class.

                  • 6. Re: Single @Deployment used across multiple TestNG test classes
                    magnus.smith

                    If you go for the abstract class having the @deployment then I would think that arquillian would redeploy the war for each concrete test class.  This would mean that your database would get scrubbed between each of the tests.

                     

                    Another approach that I have had some success with when testing a dozen ejbs with 50+ integration tests is to have the single @deployment test class with the injected ejbs but then put references to the ejbs into a helper object.  Then when I call a test method in the @deployment test class I pass the helper object with the ejb refs to another class that actually does the test.  This way I avoid having a single mega long test class by having the @deployment test class providing a facade.  

                     

                    Another thing I noticed was that the depends on method does not work in arquillian testng, but you can get around that by using groups to control the order of testing.

                    • 7. Re: Single @Deployment used across multiple TestNG test classes
                      rzvikas

                      Does anyone know if arquillian 1.0.0.Alpha5 has come up with the feature where in we need to define deployment once to test mutiple test classes.

                      • 8. Re: Single @Deployment used across multiple TestNG test classes
                        dkrizic

                        Hi Magnus,

                         

                        can you please post an example of the pattern that you have described? Arquillian is now 1.0.2.Final, but is still missing that feature so I also need to have a workaround for this.

                         

                        Regards,

                         

                        Darko

                        • 9. Re: Single @Deployment used across multiple TestNG test classes
                          magnus.smith

                          Hi Darko,

                           

                          So we require the deployment to be made just once but be able to run lots of tests against that deployment in a perscribed order.  

                          The problem is that Arquillian requires there to be a deployment for each TestNG test class that extends Arquillian making it impossible to do the usual suite approach.

                           

                          The approach that I have had success with testing EJBs is

                          1. Create a control test class that extends Arquillian that defines the @Deployment and injects the EJBs in the usual way. 
                          2. Create a holder class for the EJBs and populate it from the test class.
                          3. Create a helper class that will actually perform the test by using the EJBs from the holder class that was initialised from the test class.
                          4. Define tests in the test class using the 'groups' and 'dependsOnGroups' attributes to define the test order (note dependsOnMethods attribute does not seem to work so I use the group one)
                          5. For each test in the test class delegate the work to the helper class.

                           

                          Here is an example

                           

                          {code:java|title=Create Test Class that extends Arquillian}

                          @Test(groups = "integration")

                          public class MainTest extends Arquillian {

                           

                              @Deployment

                              public static JavaArchive createTestArchive() {

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

                                          addPackage("com.example.test").

                                          addAsManifestResource(

                                          new ByteArrayAsset("<beans></beans>".getBytes()),

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

                                          "test-persistence.xml",

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

                              }

                              @EJB

                              ProductInfoService productInfoService;

                              @EJB

                              VirtualServerService virtualServerService;

                           

                              private static ProductTestHelper productTestHelper;

                           

                               // Add the EJB refs to the holder class and create the helper

                              @Test(groups = {"init"})

                              private void init() {

                           

                                  ServiceHolder.INSTANCE.addService(productInfoService);

                                  ServiceHolder.INSTANCE.addService(virtualServerService);

                           

                                  productTestHelper = new ProductTestHelper();

                              }

                           

                           

                              @Test(groups = {Tests.CREATE_PRODUCT_INFO}, dependsOnGroups = {"init"})

                              public void testCreateProductInfo() throws Exception {

                                      productTestHelper.testCreateProductInfo();

                              }

                           

                           

                              @Test(groups = {Tests.ADD_OPTION_VALUE}, dependsOnGroups = {Tests.CREATE_PRODUCT_INFO})

                              public void testAddOptionValue() throws Exception {

                                  productTestHelper.testAddOptionValue();

                              }

                          {code}

                           

                          {code:java|title=Create Holder Class to store EJB freferences}

                           

                          /**

                          * Acts as a holder for injected stateless EJBs so that they can be accessed

                          * outside of their normal context.

                          *

                          * @author Magnus.Smith

                          * @version 1.1

                          */

                          public enum ServiceHolder {

                           

                              INSTANCE;

                           

                              private Set services = new HashSet<>();

                           

                              /**

                               * Gets the service keyed by serviceClass.

                               *

                               * @param serviceClazz The serviceClass to retrieve

                               * @return service matching given class

                               */

                              public <E> E getService(Class<E> serviceClazz) {

                                  for (Object abs : services) {

                                      if (serviceClazz.isAssignableFrom(abs.getClass())) {

                                          return (E) abs;

                                      }

                                  }

                                  throw new IllegalArgumentException(String.format(

                                          "The service %s has not been added to service holder!", serviceClazz.getName()));

                              }

                           

                              /**

                               * Adds the ejb to holder

                               * @param service

                               */

                              public void addService(Object service) {

                                  services.add(service);

                              }

                          }

                          {code}

                           

                          {code:java|title=Create Helper class to do perform the work of the tests}

                          public class ProductTestHelper {

                           

                              ProductInfoService productInfoService;

                              VirtualServerService virtualServerService;

                           

                              public ProductTestHelper() {

                                  productInfoService = ServiceHolder.INSTANCE.getService(ProductInfoService.class);

                                  virtualServerService = ServiceHolder.INSTANCE.getService(VirtualServerService.class);

                              }

                           

                           

                              public void testCreateProductInfo() throws Exception {

                                  //do test

                              }

                           

                              public void testAddOptionValue() throws Exception {

                                 // do test

                              }

                          }

                          {code}

                           

                          So using this approach you can simply create more helper classes for each area of the deployment.

                           

                          Regards,

                           

                          Magnus

                          • 10. Re: Single @Deployment used across multiple TestNG test classes
                            jkubrynski

                            Hi,

                             

                            any news about supporting multi-class test suites with Arquillian TestNG runner?

                             

                            Regards,

                            Kuba

                            • 11. Re: Single @Deployment used across multiple TestNG test classes
                              vdweij

                              I assume it is still open.

                              https://issues.jboss.org/browse/ARQ-197

                              • 12. Re: Single @Deployment used across multiple TestNG test classes
                                dan.j.allen

                                Aslak prototyped an extension that might be useful as an immediate solution.

                                 

                                https://issues.jboss.org/browse/ARQ-1216

                                 

                                He calls it "brute force Arquillian test suite"

                                • 13. Re: Single @Deployment used across multiple TestNG test classes
                                  andrewzimmer

                                  Hi Folks,

                                  In general I am really liking Arquillian; thanks for all the hard work!

                                   

                                  Maybe I'm missing something, but the functionality described in https://issues.jboss.org/browse/ARQ-1216 doesn't sound like it's about having N test classes that all rely on a single deployment. 

                                   

                                  My team is currently having a hard time getting Arquillian tests to run fast enough.  We have written a bunch of tests, but nobody runs them because the entire suite takes too long and often flames out due to OOM errors.  So we just push our code and let Bamboo (our CI build) run the tests...which is less than ideal.  But the root cause here is that despite there being a single @Deployment for our tests, every test class incurs a deploy/undeploy cycle, which adds up really quickly.

                                   

                                  The bottleneck is the fact that each test class does its own deploy and undeploy, and we could radically improve execution speed of our test suite if we could set things up so that our N testng test classes (which are all subclasses of a subclass of Arquillian, where the shared superclass defines a single "typical" @Deployment) only did deployment once.

                                   

                                  Put another way, if class X extends Arquillian, and class X has a single @Deployment, and if classes A, B, and C are all subclasses of X (and do not declare any additional @Deployments), then my dream is to have test classes A, B, and C collectively only do a single deployment.  Other ways of achieving this goal (test groups, suites, etc.) would be a welcome addition too...I'm not married to the idea of having the shared single deployment via a superclass.

                                  • 14. Re: Single @Deployment used across multiple TestNG test classes
                                    lynchmaniac

                                    I've the exact same problem that andrew have. When you are on an heavy project with multiple test, the entire suite test takes too long and it's very painfull for the developpers.

                                    So the principe to have one deployment for many test is a very good idea. I have watch the brute force Arquillian extension suite that seems very interresting but i have one or two questions.

                                    If i have many module to test im ust have one ArquillianSuiteExtension by module ?

                                    How can i launch the whole test suite in my eclipse EID ? By run As on the deployements class ?

                                    I'm really newbie on Arquillian which seem a great way to test my EJB.

                                    Is anyone have an manual end-user to use the Brute force Arquillian test suite ?