如何告诉Spring仅加载JUnit测试所需的Bean?

32

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

问题: 我的问题是,有没有一种方法可以只实例化应用程序上下文中针对特定JUnit测试需要的类?

原因: 我的应用程序上下文变得非常大。我也进行了很多集成测试,所以当我运行测试时,您会理解我在说每次都会实例化我的应用程序上下文中的所有类,这需要时间。

示例:

假设类Foo仅注入bar。

public class Foo {

@Inject
Bar bar;

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

但是应用程序上下文中有 foobar 和 bar 两个 bean。我知道这不是一个有效的应用程序上下文,但请放心,我的所有代码都可以正常工作。

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

那么我该如何告诉 Spring 只实例化 Bar 而忽略 FooBar,用于测试类 foo 呢?

谢谢。


1
你尝试过为所有测试创建一次上下文吗?或者这不是一个选项吗?https://dev59.com/bGoy5IYBdhLWcg3wguWS - Nickname0222022022
我考虑过这个问题,但是我有很多测试要做,为每个测试创建一个应用程序上下文需要时间。我希望能够直接告诉Spring我需要哪些来自所述应用程序上下文的内容。 - SandMan
哦,好的,我理解了你对问题的回答。我以为这和Vinay Veluri的回答是一样的。这个解决方案的问题在于,如果你同时运行所有测试,它可以很好地工作,而且这实际上也是我正在忙于测试的方式,但是如果你只想多次运行一个测试(比如说你正在测试某些代码,发现它不起作用,然后进行更改并再次测试),那么就需要花费时间。我正在尝试消除它所需的时间。 - SandMan
两个解决方案同时进行,我猜应该可以解决你的问题 :P 但是你不能合并它们。因此,在进行完整测试时,创建一次,但在测试时分开创建-创建最小所需上下文。我不知道是否可能,就像智能测试运行程序一样,会知道应该创建什么上下文。或者也许改变你的测试方法?在我看来,集成测试应该一次性运行所有测试用例。就像在 Jenkins 中一样。因为它们从定义上来说很长。逐个运行应该是干净的单元测试。而单元测试只需要不到一秒钟。所以我会选择使用模拟-Mockito。像往常一样。但这是离题了。 - Nickname0222022022
好的,也许我会在自己的时间里写点东西来实现我想要的。我真的很惊讶它不存在。当然单元测试可以正常工作,但在将我的工作推送到开发之前,我想看到它按照预期工作。无论如何,祝你一切顺利。 - SandMan
显示剩余5条评论
3个回答

9
考虑将default-lazy-init="true"添加到您的spring上下文xml bean标签中(或向那些需要长时间启动的具体bean添加lazy-init="true")。 这将确保仅创建使用applicationContext.getBean(class-or-bean-name)调用或通过@Autowired/@Inject注入到您的测试中的那些bean。(其他一些类型的bean如@Scheduled bean将仍然被创建,但您需要检查是否存在问题) (如果您使用spring Java配置,请将@Lazy添加到配置文件中) 注意 - 如果有一个bean没有显式地使用applicationContext.getBean()初始化或作为由使用applicationContext.getBean()获得的bean使用的依赖项注入,则该bean将不再被构造或初始化。根据您的应用程序,这可能会导致失败或不失败。也许您可以选择性地将这些bean标记为lazy-init="false"

7
是的,我们可以通过每个测试用例使用上下文(context)来实现。准备一个包含测试用例所需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 {}

1
我已经考虑过了,但是我有很多测试需要进行,因此为每个测试创建一个应用程序上下文需要时间。我希望只需告诉Spring我需要哪些来自该应用程序上下文的内容。 - SandMan
2
这个有没有基于注解的方法? - Akhil Surapuram
你好,如何编写test-context-case1.xml以自动包含某些包内的所有文件(例如com.company.project.module_one)?我知道可以使用@ComponentScan来实现,但似乎在这里不起作用...谢谢! - ch271828n

6

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

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

  1. 像VinayVeluri回答得那样,创建单独的上下文并在每个测试中分别启动它们。

  2. 为所有测试创建一个上下文。就像这里:重用junit测试类之间的spring应用程序上下文 对于同时进行所有测试的测试而言,这是一个很大的优化。

  3. 混合前两个点。仅为测试目的创建一个较小的上下文。模拟从未被测试过但可能会抛出NPE等异常的内容。就像这里:将Mockito模拟注入Spring Bean中 来加速上下文构建。然后像第2点那样重复使用它。所有测试只需构建一次。就我个人而言,我会选择这个。

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


2
广告4:你真的无法自动完成这个任务。当Bean被创建时,它可能会运行一些代码,无法自动决定该代码是否重要。虽然这样做是糟糕的编码实践,但你可以有一个Bean在创建时向数据库添加一些记录,然后代码的某个部分稍后读取该记录。作为开发人员,你可能能够发现这些依赖关系,但无法自动完成此操作 - 这将相当于停机问题或更糟的情况。 - Michał Kosmulski
广告4:虽然测试并不总是能够告诉我们应用程序是否“正确”,但我们可以自动生成“建议的最小上下文”并尝试运行测试。只要测试通过,上下文就是正确的。看看“变异”测试,它可以发现你的测试是否真正测试了某些可能会出问题的东西...例如,https://pitest.org/ - Lubo

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