0

I'm trying to wrap my head around Arquillian and perhaps even start using it in my project. I have a simple Java web app that deploys as a WAR to Tomcat.

In my project, I define a ServletContextListener impl so that I can execute code when Tomcat starts and stops the application.

I'm trying to write a super-simple Arquillian test class that uses ShrinkWrap and:

  1. Confirms that my bundled WAR can be deployed to Tomcat and started without throwing exceptions; and
  2. Can access a simple system property once the app is running (that the ServletContextListener checks for); and
  3. Confirms that when Tomcat shuts down, no exceptions are thrown (clean shutdown)

Also, my class that implements ServletContextListener is called AppLifecycleManager:

public class AppLifeCycleManager implements ServletContextListener {
    private String logLevel;

    // Injected by Guice, but that's not really relevant for this question.
    @Inject
    private Logger logger;

    // Getter and setter for logLevel and logger

    @Override
    public void contextInitialized(ServletContextEvent event) {
        logLevel = System.getProperty("log.level");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        logger.info("Peacefully shutting down the application.");
    }
}

So far, here's my best attempt:

@RunWith(Arquillian.class)
public class MyFirstRealIntegrationTest {
    @Deployment
    public static Archive<?> createDeployment() {
        // Haven't figured this part out yet, but for the sake of
        // this question lets pretend this returns a properly-packaged
        // WAR of my web app, the same that my Ant build currently produces.
    }

    @Test
    public void shouldBeAbleToStartTomcatWithoutExceptions() {
        // Given
        Archive war = createDeployment();

        // When - deploy war to Tomcat container
        try {
            // ??? how to access/init a Tomcat container?
            TomcatContainer tomcat = new TomcatContainer(); // this is wrong
            tomcat.start();
        } catch(Throwable throwable) {
            // Starting the container should not throw exceptions
            Assert.fail();
        }
    }

    @Test
    public void shouldBeAbleToStopTomcatWithoutExceptions {
        // Same setup as above test but stops tomcat and checks for
        // thrown exceptions. Omitted for brevity.
    }

    @Test
    public void shouldHaveAccessToSysPropsOnceRunning() {
        // Here, deploy to the container and start it.
        // Then, confirm that AppLifecycleManager correctly read
        // the log.level system property.

        // Given
        Archive war = createDeployment();
        TomcatContainer tomcat = new TomcatContainer();

        // When - AppLifeycleManager should now read the system property
        tomcat.start();

        // Then - make sure log.level was set to "DEBUG" and that it was
        // correctly read by AppLifeCycleManager.
        Assert.assertTrue(war.getClass(AppLifeCycleManager.class)
                .getLogLevel().equals("DEBUG"));
    }
}

So, given my approach here, I immediately have several problems:

  1. I'm not sure how to access/instantiate my Tomcat container so that it can even be started/stopped
  2. I'm not sure how to actually execute tests from inside my running/deployed web app. In the 3rd test above I used war.getClass(AppLifeCycleManager.class).getLogLevel() to try and get access to a "live" class instance and check its logLevel property's runtime value, but I know this is wrong.

So I ask: how would a battle-worn Arquillian veteran write these 3 simple tests, and how do I actually go about performing tests on my "running" web app from inside the JUnit test? Thanks in advance.

IAmYourFaja
  • 55,468
  • 181
  • 466
  • 756

2 Answers2

2

I dont't think you should handle the startup/shutdown of tomcat in your test. I would by much easier if you used the embedded tomcat container: https://docs.jboss.org/author/display/ARQ/Tomcat+7.0+-+Embedded. In this case, arquillian will handle the startup and shutdown of tomcat.

You should create the deployment in the method annotated with @Deployment. Just read the following guide: http://arquillian.org/guides/getting_started/

cremersstijn
  • 2,375
  • 4
  • 28
  • 41
  • Thanks @cremersstijn (+1) - a few thoughts on your answer: (1) Can you confirm that I understand the role/function of Arquillian correctly; that Arquillian allows you to "deploy" an in-memory version of your application to an in-memory version of its container? And (2) I see what you mean by letting Arquillian handle startup and shutdown, but what about my actual tests? How would I write my `shouldHaveAccessToSysPropsOnceRunning` test? I think seeing that simple example will turn a lot of lightbulbs on for me. Thanks again! – IAmYourFaja Feb 05 '13 at 09:06
  • 1) " to an in-memory version of its container": it depends, if the container adapter is "embedded" that it will be in memory, if the type is "managed" arquillian will startup and shutdown a container that you have installed, and if the type is remote, arquillian just connect to this server, you should startup this type of container. – cremersstijn Feb 05 '13 at 14:56
1

You're almost there. createDeployment manages the lifecycle of your embedded container for you (starts and stops the virtual container automatically). That way, you're just focussing on the tests themselves. To write your integration tests, there's no "framework" or "Arquillian API" to code against. You just call classes and methods the way your main code does.

The key thing here is: your tests are actually running inside the container. If an exception occurs, or if your assert fails, the Arquillian runner throws an exception and stops the container. For your code example, where you're testing to see if you can read a system property:

@RunsWith(Arquillian.class)
public class MyArquillianTest {
    @Deployment
    public Archive<?> createDeployment() { ... }

    @Test
    public void shouldBeAbleToReadSysPropAtStartup() {
        Assert.assertTrue(System.getProperty("log.level") != null);
    }
}

Remember, Assert.assertTrue(System.getProperty("log.level") != null) Arquillian is "transporting" your code inside the container you configure it for. So that assert is actually running inside your deployed container.