问题 如何告诉spring只加载JUnit测试所需的bean?


一个可能有高级答案的简单问题。

问题: 我的问题是,有没有办法在应用程序上下文中仅实例化特定JUnit测试所需的类?

原因: 我的应用程序环境变得越来越大。我也做了很多集成测试,所以你猜我会理解当我说每次运行测试时,我的应用程序上下文中的所有类都会被实例化,这需要时间。

这个例子:

说类Foo只注入吧

public class Foo {

@Inject
Bar bar;

@Test
public void testrunSomeMethod() throws RegisterFault {
    bar.runSomeMethod();
}

但是应用程序上下文有bean foobar和bar。我知道这不是一个虚拟应用程序上下文,但请确保我的所有代码都能正常工作。

<beans>
     <bean id="foobar" class="some.package.FooBar"/>
     <bean id="bar" class="some.package.Bar"/>
<beans>

那么我如何告诉spring只实例化Bar并忽略测试类foo的FooBar。

谢谢。


7715
2017-08-18 09:26


起源

您是否尝试过每次测试创建一次上下文?或者它不是一个选项? stackoverflow.com/questions/8501975/... - Sarseth
我考虑过它,但我有很多测试,所以为每个测试创建一个应用程序上下文需要时间。我希望从所述应用程序上下文中告诉spring我需要什么 - SandMan
我想我们有误会。我的意思是,创造一次。因此,您不要为每个测试创建上下文。您之前创建上下文然后在此创建的上下文上启动测试。如果这不是选项,因为你在bean中进行了一些更改,你需要重置它们,那么VinayVeluri的回答是好的。但是你需要创建单独的xml文件,看起来有点烦人,你有100个上下文文件。如果你想要每个测试的单独上下文o0或混合解决方案;) - Sarseth
哦,好的,我理解你对这个问题的回答。我以为它和Vinay Veluri一样。看到这个解决方案的问题是。如果您同时运行所有测试并且实际上我正在忙于测试,但是如果您只是想多次运行一次测试(比如说您正在忙着测试代码,某些东西不起作用,那么您可以进行更改,然后测试再次)这需要时间。我试图消除它所花费的时间 - SandMan
两个解决方案同时会解决你的问题我猜:P但你不能合并它们。因此,当完整测试进行时,创建一次,但是当单独测试时 - 创建最少的需要上下文。我不知道是否有可能像智能测试运行器那样知道它应该创建什么上下文。或者可能改变你的测试方法?集成测试imho应该立即运行。就像詹金斯一样。因为他们的定义很长。一个接一个地运行,应该是干净的单元测试。单元测试不到第二次。所以我会去嘲笑 - Mockito。一如既往。但这是非现实的。 - Sarseth


答案:


这不是直接的答案,所以我不会将其标记为解决方案。但希望它有所帮助。

一般来说,我看到三个选项

  1. 正如VinayVeluri很好地回答。创建单独的上下文并分别在每个测试中启动它们。

  2. 每次测试都创建一次上下文。就像这里一样: 在junit测试类中重用spring应用程序上下文 这是一次性测试所有测试的重大优化。

  3. 混合这两个第一点。创建一个较小的上下文仅用于测试目的。嘲笑,什么从未经过测试,但可以抛出NPE等。像这里: 将Mockito模拟注入Spring bean 加强上下文构建。并在第2点重新使用它。一次性构建所有测试。就个人而言,我会选择那个。

  4. 这个等待关于某种智能测试运行器的答案,它为每个测试创建最少的所需上下文。


2
2017-08-18 12:22



广告。 4:你不能自动地这样做。 bean在创建时可能会运行一些代码,并且无法自动判断此代码​​是否重要。虽然编码很糟糕,但是你可以创建一个bean,在创建时将一些记录添加到数据库中,然后某些部分代码会在稍后读取该记录。作为开发人员,您可能能够发现这种依赖关系,但您无法自动执行 - 这相当于停止问题或更糟。 - Michał Kosmulski


考虑添加 default-lazy-init="true" 到你的spring上下文xml beans标签(或添加 lazy-init="true" 那些需要很长时间才能启动的特定豆类。 这将确保只创建那些使用applicationContext.getBean(class-or-bean-name)调用或通过注入的bean @Autowired / @Inject 进入你的测试。 (像其他一些类型的豆类 @Scheduled 但是仍会创建bean,但你需要检查是否存在问题)

(如果使用spring Java配置,请添加 @Lazy 到配置文件)

警告  - 如果有一个bean没有使用applicationContext.getBean()显式初始化,或者被注入为使用applicationContext.getBean()获得的bean所使用的依赖项,则该bean将不再构造或初始化。根据您的应用程序,这可能导致事情失败或不。也许你可以选择性地将这些豆子标记为 lazy-init="false"


6
2018-05-02 10:22





是的,我们可以使用每个测试用例的上下文来做到这一点。使用测试用例所需的bean准备测试上下文xml文件。

如果您使用maven,请将test-context.xml放在下面 src/test/resources 夹。

使用以下注释注释所需的测试类

@ContextConfiguration(locations = "classpath:test-application-context.xml")

这有助于仅加载测试用例的特定bean。

如果你有两种测试用例,那么

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case1.xml")
public class TestClassCase1 {}

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case2.xml")
public class TestClassCase2 {}

3
2017-08-18 09:45



我考虑过它,但我有很多测试,所以为每个测试创建一个应用程序上下文需要时间。我希望从所述应用程序上下文中告诉spring我需要什么 - SandMan