Spring测试中的@ContextConfiguration和静态上下文

3

我有一个抽象测试类的以下代码(我知道XmlBeanFactoryClassPathResource已经过时,但这不太可能是问题的原因)。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public abstract class AbstractIntegrationTest {

    /** Spring context. */
    protected static final BeanFactory context = new XmlBeanFactory(new ClassPathResource(
            "com/.../AbstractIntegrationTest-context.xml"));

    ...

}

它会加载默认的测试配置XML文件AbstractIntegrationTest-context.xml(然后我使用自动装配)。我还需要在用@BeforeClass@AfterClass注释的静态方法中使用Spring,因此我有一个单独的上下文变量指向相同的位置。但问题是这是一个单独的上下文,它将具有不同的bean实例。那么,如何合并这些上下文或如何从我的静态上下文调用由@ContextConfiguration定义的Spring bean初始化呢?
我想到了通过摆脱那些静态成员来解决这个问题,但我很好奇是否可以通过对代码进行相对较小的更改来解决它。
2个回答

8
您是正确的,您的代码将产生两个应用程序上下文:一个由@ContextConfiguration注释启动、缓存和维护。第二个上下文是您自己创建的。同时使用两个上下文没有太多意义。
不幸的是,JUnit不太适合集成测试,主要是因为您无法使用非静态before classafter class方法。我看到您面临两个选择:
  • 切换到 - 我知道这是一个重大的转变。

  • 在仅在测试期间包含的Spring bean中编码设置/拆卸逻辑 - 但然后它只运行一次,在所有测试之前。

还有一些不太优雅的方法。您可以使用static变量并将上下文注入其中:
private static ApplicationContext context;

@AfterClass
public static afterClass() {
    //here context is accessible
}

@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
    context = applicationContext;
}

或者你可以使用@DirtiesContext注释你的测试类,并在一些测试bean中进行清理:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext(classMode = AFTER_CLASS)
public abstract class AbstractIntegrationTest {

    //...

}

public class OnlyForTestsBean {

    @PreDestroy
    public void willBeCalledAfterEachTestClassDuringShutdown() {
        //..
    }

}

关于第二个选择。在所有测试之前运行一次并不是一个大问题。问题是,如何执行“类后”逻辑? - Vic
第一个解决方案仍然存在问题:“从哪里获取应用程序上下文以进行注入?”第二个必须有效。我会尝试它。 - Vic
@Vic:在使用setApplicationContext()的解决方案中,应用程序上下文通过@ContextConfiguration加载并将上下文分配给静态变量。原则上应该可以工作。 - Tomasz Nurkiewicz

7

我不确定你是否已经选定了任何方法,但我遇到了相同的问题,并使用Spring测试框架的TestExecutionListener以另一种方式解决它。

beforeTestClassafterTestClass,因此两者都等同于JUnit中的@BeforeClass@AfterClass

我的做法:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(Cleanup.class)
@ContextConfiguration(locations = { "/integrationtest/rest_test_app_ctx.xml" })
public abstract class AbstractIntegrationTest {
    // Start server for integration test.
}

您需要创建一个继承AbstractTestExecutionListener的类:
public class Cleanup extends AbstractTestExecutionListener
{

   @Override
   public void afterTestClass(TestContext testContext) throws Exception
   {
      System.out.println("cleaning up now");
      DomainService domainService=(DomainService)testContext.getApplicationContext().getBean("domainService");
      domainService.delete();

   }
}

通过这样做,您可以访问应用程序上下文,并在此处使用Spring Bean进行设置/拆卸。
希望这可以帮助像我一样使用JUnit + Spring进行集成测试的任何人。

感谢提供另一种解决方案。就我所记得的,我选择了Tomasz的方法。 - Vic
使用@TestExecutionListeners注释似乎替换了默认监听器的使用(例如,您将失去依赖注入); 有什么办法可以将内容追加到默认设置中吗? - Matthew Wise
1
@MatthewWise 你可以指定监听器的合并模式:@TestExecutionListeners(value = MyListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) - Oliver Hernandez
@OliverHernandez 我一定错过了那个属性,谢谢! - Matthew Wise
谢谢谢谢!谢谢!你的例子让我解决了一个不同但相关的问题,这个问题让我困扰了两年:如何让一个遗留代码适配器与大量测试套件中存在的许多Spring上下文保持同步。请参阅相关问题Spring静态上下文访问器和集成测试,以及我在那里的答案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接