1 Reply Latest reply on May 16, 2010 4:40 AM by Aslak Knutsen

    Deciding how to mark in-container vs client

    Dan Allen Master

      We've got our terminology mixed up with regard to where the test is executed. We've tried to sort it out in ARQ-126, but a recent discussion with ALR revealed that perhaps we need to look at the problem from further out.

       

      Arquillian's motto is to test in-container. There are actually two interpretations, and hence two possible run modes in Arquillian.

       

      In the first run mode, the code being tested is packaged into an archive (i.e., @Deployment), deployed to the server, and the test case exercises the deployed code as a client (e.g., HTTP, remote EJB invocation). In this case, the code being tested is running in-container, but not the test case.

       

      In the other run mode, the test case and the test framework is packaged into an archive along with the code being tested. The combined archive is deployed to the server and the test case executes inside the context of the container through remote a remote protocol (e.g., HTTP). The test case is just another component in the system and can inject the components under test directly. In this case, the code being tested and the test case are both running in-container.

       

      (Not that I've avoided the terms local and remote to describe these modes. Those terms depend too much on context and therefore cause confusion when used to describe what is going on).

       

      The tricky part about the second case is that it is subject to the Heisenberg uncertainty principle. In order for the test case to run in-container, the deployment archive has to be modified to include the test case, the test framework and the remote protocol endpoint. This modifies the deployment and effects the nature of the code under test (it can affect global JNDI names, for instance).

       

      Arquillian 1.0.0.Alpha1 assumed that the @Deployment method returned a EAR module and would package it instead of an EAR named test.ear and deploy it to the server. The trouble is that we were making assumptions about what the developer wanted to deploy.

       

      In trunk, Arquillian now honors the test developer's archive choice (e.g., EnterpriseArchive) and tries to weave in the test artifacts intelligently. But the fact remains, the user's archive is being modified. And we should take care to communicate this through the metadata.

       

      In ARQ-126, we were discussing the @Run annotation and it's possible values. The @Run annotation and packaging are tightly coupled. Running the test as a client is the same as not packaging the test in the archive. Running the test in-container requires packaging the test in the archive. (Except for local bean containers, in which case packaging is not necessary and everything runs locally).

       

      With all that said, we would like to offer some proposals for how to set the run mode, with some focusing on the packaging aspect. We also want to clearly document what happens to the deployment in the in-container mode so that the developer is not confused when what is being deployed has been modified from the original @Deployment.

       

      Option 1: In terms of run-mode

       

      // deploys test.jar
      @Deployment @Run(AS_CLIENT)
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      
      // deploys test.ear containing test.jar as a module
      @Deployment @Run(IN_CONTAINER)
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      

       

      Option 2: In terms of packaging

       

      // deploys test.jar
      @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
       
       
      // deploys test.ear containing test.jar as a module
      @PackageTest @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      
      
      // deploys mytest.ear containing test.jar
      @PackageTest(archiveName = "mytest.ear", type = EnterpriseArchive.class) @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      
      

       

      Option 3: In terms of package relationship

       

       

      // deploys test.jar
      @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
       
       
      // deploys test.ear containing test.jar as a module
      @BundleTest @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      
      
      // deploys mytest.ear containing test.jar as a module
      @BundleTest(archiveName = "mytest.ear", type = EnterpriseArchive.class) @Deployment
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      

       

      Option 4: Use @Package as customization w/ @Run

       

       

      // deploys test.jar
      @Deployment @Run(AS_CLIENT)
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      
      // deploys mytest.ear containing test.jar as a module
      @Deployment @Run(IN_CONTAINER) @TestArchive(archiveName = "mytest.ear", type = EnterpriseArchive.class)
      public static JavaArchive createTestArchive() {
         return ShrinkWrap.create("test.jar", JavaArchive.class);
      }
      

       

      I'd like to just get some thoughts from the team members and community about how best to communicate the pure deployment (test case as client) vs packaged deployment (test case in-container).

        • 1. Re: Deciding how to mark in-container vs client
          Aslak Knutsen Master

          this was suppose to be my initial comments on some internal/spi design issues we have atm, but you 'jumped the gun' on it so i'll just add them here..

           

           

           

          Today, the DeployableContainers have to return a ContianerMethodExecutor on deploy pr SPI. This is used to communicate/execute the TestCase in a container defined way. The ContainerMethodExecutor is what we call a Protocol implementation. Currently we have two implementations; Servlet and Local.

           

          At the design time we were lacking Configuration and a Context, so the ContainerMethodExecutor contained the information on how to execute the TestCase 'in-container'.

           

          With the new Client/Local RunMode,  Arquillian simply 'override/replace' the ContainerMethodExecutor returned by the DeployableContainers with a Local version, ignoring the SPI contract.

           

           

          There are a couple of problems with this design:

           

          • The intent was always to have multiple protocol implementations, servlet, ejb, jmx etc.. Some target contianers will support more then just one protocol and it should be up to the user to choose which protocol is uses.

           

          • The protocol is just another Auxiliary Archive for the Packager to handle. But the choosen protocol strongly influence what the Packager should produce. If we choose a Servlet protocol, there has to be a WebArchive involved. We have two different versions of the Servlet Protocol, one for EE5 and one for EE6. The difference is; with EE5 it returns a WebArchive with a good old web.xml, and in EE6 it returns a JavaArchive with a web-fragment.xml. But the only way the Packager knows if we're running in EE5 or EE6 mode is if any of the Auxiliary Archives are a WebArchive. So the new EEDeploymentPackager in alpha-2 has the following rules:

           

          @DeploymentServlet EE vEndResultAction
          JavaArchiveEE5EnterpriseArchiveCreate a new EnterpriseArchive, add Deployment and ServletProtocl as module, the other Auxiliary Archives as libraries.
          WebArchiveEE5ExceptionCan not merge two WebArchives and both packed in a EnterpriseArchive will result in isolation issues.
          EnterpriseArchiveEE5EnterpriseArchiveSame as JavaArchive, but using the Deployment defined EnterpriseArchive instead of creating a new.
          JavaArchiveEE6WebArchiveCreates a new WebArchive, adds Deployment and Auxiliary Archives as libraries.
          WebArchiveEE6WebArchiveAdds Deployment and Auxiliary Archives as libraries.
          EnterpriseArchiveEE6EnterpriseArchiveFinds the protocol JavaArchive based on name(arquillian-protocol.jar), wraps this jar in a WebArchive and adds it as a module. The other Auxiliary Archives are added as libraries.

           

          • This is error prone, not very intiuative and it influenses the defined Deployment in a heavy way(jndi names etc).
          • Based on the RunMode, Arquillian ignores what the DeployableContainer has defined.
          • The DeployableContainer has some of the MetaData on how to communicate with the deployment. E.g: bind ip, bind port, protocols, web contexts etc. This is MetaData the Protocol implementation needs to setup the correct communication with the deployed contianer.