简体   繁体   中英

Why is TestContext.getInstance() is null in beforeTestClass and afterTestClass methods in TestExecutionListener?

I have TestNG tests that use an instance of a class called Server. This instance is autowired in my tests using the Spring Test framework. As suggested here , I would like to externalize start and stop of the Server in beforeTestClass() and afterTestClass() methods by implementing the TestExecutionListener interface. To do this, I first use Java reflection to get the Server fields in the test class and then invoke start and stop in beforeTestClass() and afterTestClass() methods respectively. The TestContext that is passed as an argument in to these methods encapsulates the instance of the test. However the TestContext.getInstance is null, which means that I cannot get the value of the field (a Server object) at runtime.

Here is an example:

public class Server
{
    public void start()
    {
        System.out.println("Started");
    }

    public void stop()
    {
        System.out.println("Stopped.");
    }
}

@Configuration
public class ServerConfig
{
    @Bean
    public Server server()
    {
        return new Server();
    }
}

public class ServerStartStopListener implements TestExecutionListener, Ordered
{
    @Override
    public void afterTestClass(TestContext testContext)
    {
        //testContext.getTestInstance() is null                                                                                                                                    
    }

    @Override
    public void afterTestMethod(TestContext testContext){}

    @Override
    public void beforeTestClass(TestContext testContext)
    {
        //testContext.getTestInstance() is null                                                                                                                                    
    }

    @Override
    public void beforeTestMethod(TestContext testContext){}

    @Override
    public void prepareTestInstance(TestContext testContext){}

    @Override
    public int getOrder()
    {
        return 6000;
    }
}

@ContextConfiguration(classes={ServerConfig.class})
@TestExecutionListeners(listeners=ServerStartStopListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class AppTest extends AbstractTestNGSpringContextTests
{
    @Autowired
    private Server server;

    @Test
    public void test()
    {
        System.out.println("Server should be started at this point");
   }
}

There are workarounds. This suggests to the autowired bean from the test instance's ApplicationContext. Another one is to use beforeTestMethod() and afterTestMethod() methods. However, they do not suit my needs.

As per beforeTestClass() and afterTestClass() methods in org.springframework.test.context.TestContextManager, the above behaviour seems intended. Could someone explain why?

This question is related to 10184602 . Please see the discussion there for further details.

But to answer your question...

As per beforeTestClass() and afterTestClass() methods in org.springframework.test.context.TestContextManager, the above behaviour seems intended. Could someone explain why?

As per the Javadoc, the TestContext encapsulates the context in which a test is executed, agnostic of the actual testing framework in use . The semantics are important here: the TestContext does not encapsulate the test instance, per se. For example, in the beforeTestClass() and afterTestClass() methods of a TestExecutionListener , you are not given access to the test instance since those methods are limited to interacting with the test class by design. These semantics are required so that the Spring TestContext Framework can be used with TestNG and JUnit.

I haven't tried this, but theoretically... as a work-around, you could consider the following:

  1. Extend from AbstractTestExecutionListener .
  2. Override prepareTestInstance() , store the test instance in a field within your custom TestExecutionListener , and execute your start code.
  3. Override afterTestClass() , and execute your stop code using the test instance reference set in prepareTestInstance() .

Of course, you might need to be careful with regard to the ordering of " test method " execution within your TestNG suite. I say " test method " since many developers get confused by the run-time semantics of TestNG regarding dependencies on actual test methods, before methods, etc. When in doubt, turn on DEBUG logging for org.springframework.test.context and verify that the methods within your TestExecutionListener are getting executed when you expect them to (ie, not out of order), relative to the ordering of " test methods ".

Regards,

Sam (author of the Spring TestContext Framework)

ps Your custom TestExecutionListener will naturally need to be declared after the DependencyInjectionTestExecutionListener .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM