JUnit 4: Set up things in a test suite before tests are run (like a test's @BeforeClass method, just for a test suite)
Asked Answered
K

4

25

I want to do some functional testing on a (restful) webservice. The testsuite contains a bunch of test cases, each of which performs a couple of HTTP requests on the webservice.

Naturally, the webservice has to run or the tests will fail. :-)

Starting the webservice takes a couple of minutes (it does some heavy data lifting), so I want to start it as infrequently as possible (at least all test cases that only GET resources from the service could share one).

So is there a way to do set up me the bomb in a test suite, before the tests are run like in a @BeforeClass method of a test case?

Krum answered 8/12, 2008 at 15:23 Comment(0)
A
27

The answer is now to create a @ClassRule within your suite. The rule will be invoked before or after (depending on how you implement it) each test class is run. There are a few different base classes you can extend/implement. What is nice about class rules is that if you do not implement them as anonymous classes then you can reuse the code!

Here is an article about them: http://java.dzone.com/articles/junit-49-class-and-suite-level-rules

Here is some sample code to illustrate their use. Yes, it is trivial, but it should illustrate the life-cycle well enough for you to get started.

First the suite definition:

import org.junit.*;
import org.junit.rules.ExternalResource;
import org.junit.runners.Suite;
import org.junit.runner.RunWith;


@RunWith( Suite.class )
@Suite.SuiteClasses( { 
    RuleTest.class,
} )
public class RuleSuite{

    private static int bCount = 0;
    private static int aCount = 0;

    @ClassRule
    public static ExternalResource testRule = new ExternalResource(){
            @Override
            protected void before() throws Throwable{
                System.err.println( "before test class: " + ++bCount );
                sss = "asdf";
            };

            @Override
            protected void after(){
                System.err.println( "after test class: " + ++aCount );
            };
        };


    public static String sss;
}

And now the test class definition:

import static org.junit.Assert.*;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;

public class RuleTest {

    @Test
    public void asdf1(){
        assertNotNull( "A value should've been set by a rule.", RuleSuite.sss );
    }

    @Test
    public void asdf2(){
        assertEquals( "This value should be set by the rule.", "asdf", RuleSuite.sss );
    }
}
Adhesion answered 3/10, 2011 at 18:12 Comment(2)
" The rule will be invoked before or after (depending on how you implement it) each test class is run" - I can't confirm that. The evaluate() method of my ClassRule has only been called once, although I had added two Test classes to the suite.Takamatsu
Indeed, the rule will be invoked before or after test class is run depending on certain implementation. In the example provided by @ArtB the rule implemented at suite level. So it will be called one time before all tests in suite are run and one time after all tests in suite are run. So @ArtB's answer works fine.Enunciate
I
1

jUnit can't do that sort of thing -- though TestNG does have @BeforeSuite and @AfterSuite annotations. Normally, you get your build system to do it. In maven, there are the "pre-integration-test" and "post-integration-test" phases. In ANT, well you just add the steps to the task.

Your question is pretty much a dup of Before and After Suite execution hook in jUnit 4.x, so I'd take a look at the suggestions over there.

Identify answered 8/12, 2008 at 15:28 Comment(0)
A
0

One option is to use something like Apache Ant to launch your unit test suite. You can then put a target invocation before and after your junit target to start and stop your webservice:

<target name="start.webservice"><!-- starts the webservice... --></target>
<target name="stop.webservice"><!-- stops the webservice... --></target>
<target name="unit.test"><!-- just runs the tests... --></target>

<target name="run.test.suite" 
        depends="start.webservice, unit.test, stop.webservice"/>

You then run your suite using ant (or your integration tool of choice). Most IDEs have Ant support, and it makes it much easier to move your tests into a continous integration environment (many of which use Ant targets to define their own tests).

Acromion answered 9/12, 2008 at 12:28 Comment(0)
C
-3

As an aside, it's a bad idea to have unit tests actually calling external resources like webservices, databases, etc.

Unit tests should be super-quick to run and a delay of 'a couple of minutes' for each run of the suite will mean it won't be run as much as it should.

My advice:

Look at mocking external dependencies in unit tests with something like EasyMock (http://www.easymock.org/).

Build a seperate suite of integration tests with something like Fitnesse (http://fitnesse.org/) or a homegrown solution that runs against a test environment and which is continually up.

Carious answered 9/12, 2008 at 11:52 Comment(1)
hi nick, my unit tests don't do that type of stuff and are superquick. i was talking about functional tests :-)Krum

© 2022 - 2024 — McMap. All rights reserved.