How to run a set of code before and after any test

2019-09-01 07:49发布

问题:

Here my test code:

public class Test1 {
    @BeforeClass
    public void setUp() {
        EmbeddedTomcat.start();
    }

    // test methods...

    @AfterClass
    public void tearDown() {
        EmbeddedTomcat.stop();
    }      
}

public class Test2 {
    @BeforeClass
    public void setUp() {
        EmbeddedTomcat.start();
    }

    // test methods...

    @AfterClass
    public void tearDown() {
        EmbeddedTomcat.stop();
    }
}

// ...

As you can see, I duplicate the start and stop calls to embedded tomcat in each Test class. How can I avoid this duplication with JUnit ?

Requirement: - The start and stop calls must be called ONCE for all test classes

JUnit 4.11

回答1:

In addition to making a parent class that runs the @BeforeClass and @AfterClass. you can create a JUnit Rule. You can make your rule subclass ExternalResource which has methods to override for adding behavior before and after executions (class or tests).

public class EmbeddedTomcatServer extends ExternalResource {

            ...

            @Override
            protected void before() throws Throwable {
                    EmbeddedTomcat.start();
            };

            @Override
            protected void after() {
                    EmbeddedTomcat.stop();
            };
    }

Then in your test use the @ClassRule which runs the server's before() and after() methods during BeforeClass and AfterClass time.

public class Test1 {

   @ClassRule
   public static EmbeddedTomcatServer tomcat = new EmbeddedTomcatServer();

   //tests as normal, no more @BeforeClass or @AfterClass

}

public class Test2 {

   @ClassRule
   public static EmbeddedTomcatServer tomcat = new EmbeddedTomcatServer();

   //tests as normal, no more @BeforeClass or @AfterClass

}

This solution might be better than the using a parent class because you can use this Rule in any test without having to change the parent class.

EDIT only running the start() and stop() once

It is a little unclear if you mean running the start and stop once per test class (as your original code using @BeforeClass and @AfterClass)

Or if you want to run start and stop only once over ALL of the tests across the all the test classes that are run.

If you want to do the latter, @ClassRules are helpful here too. Just create a test suite with the test classes to use and then put the ClassRule ONLY IN THE SUITE CLASS

@RunWith(Suite.class)
@SuiteClasses({ Test1.class, Test2.class })
public class AllTests {
   @ClassRule
   public static EmbeddedTomcatServer tomcat = new EmbeddedTomcatServer();

}

This will start tomcat when the AllTests suite starts and then stop tomcat when all the tests in the test suite have run.



回答2:

If possible for your project, you can create a TestParent class which defines these @BeforeClass and @AfterClass methods and make your Test1 and Test2 classes extend from it.

public class TestParent {
    @BeforeClass
    public void setUp() {
        EmbeddedTomcat.start();
    }

    @AfterClass
    public void tearDown() {
        EmbeddedTomcat.stop();
    }    
}

public class Test1 extends TestParent {
    //...
}

public class Test2 extends TestParent {
    //...
}