【问题标题】:How to re-initialize my bean如何重新初始化我的 bean
【发布时间】:2010-12-06 22:00:03
【问题描述】:

我的 applicationContext-test.xml 中有一个 bean,用于模拟外部搜索引擎。这样,当我运行测试时,只要我的应用程序代码引用这个搜索引擎,我就知道我使用的是我的模拟引擎而不是真正的引擎。

我面临的一个问题是我希望这个引擎在不同的场景中表现不同。例如,当我调用getDocuments() 时,我通常希望它返回文档。但有时我希望它抛出异常以确保我的应用程序代码正确处理异常。

我可以通过在我的测试代码中引用 bean 并更改一些存根来实现这一点,但是我必须将存根改回原来的样子,这样我的其他测试也能通过。由于许多原因,这似乎是一种不好的做法,所以我正在寻找替代方法。

我考虑过的一种替代方法是完全重新初始化 bean。 bean 使用静态工厂方法从 applicationContext-test.xml 初始化。我想做的是:

  1. 从我的测试代码中引用 bean 来更改它的一些存根
  2. 使用这些新存根运行测试
  3. 在此测试结束时,使用 applicationContext-test.xml 中指定的静态工厂方法重新初始化 bean

我尝试过这样的事情:

    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
            new String[] { "applicationContext-test.xml" });
    Factory factory = appContext.getBean(Factory.class);
    factory = EngineMocks.createMockEngineFactory();

但这并不能解决问题。在此之后运行的任何测试仍然会失败。似乎我的新 factory 变量包含我想要的 Factory 并相应地运行,但是当在其他地方引用 bean 时,getDocuments() 仍然会抛出之前存根的异常。显然,我的重新初始化只影响了局部变量,而不影响 bean 本身。

谁能告诉我如何实现我的目标?

更新

虽然我很欣赏有关如何编写更好的测试和更好的模拟的建议,但我的目标是重新初始化 bean。我相信无论是否适合我的用例,学习如何做到这一点都是有价值的(我相信它确实适合我的用例,但我很难在这里说服我的一些批评者)。

唯一能让我投赞成票或绿色勾号的答案是那些建议我如何重新初始化我的 bean 的答案。

【问题讨论】:

  • 我的回答会有点取决于您当前如何创建测试替身(例如模拟、存根)?它是手工制作的,还是您使用的是 easymock 或 mockito 之类的?
  • 我的模拟是使用 Mockito 创建的,它们是通过 applicationContext 设置的。换句话说,我不会为我编写的每个测试制作模拟,而是我的应用程序代码引用了一个 @Resource,因此当我运行测试时,我加载我的应用程序上下文,以及引用的每一段应用程序代码该 bean 现在使用的是模拟版本而不是真实版本。

标签: java unit-testing spring-mvc dependency-injection mockito


【解决方案1】:

您应该定义需要结果的情况和需要例外的情况。它们应该通过方法的输入参数来区分。否则就不是一个好的测试。因此,对于给定的一组参数,输出应该是可预测的。

【讨论】:

  • 我不确定你的意思。这确实是两个不同的测试用例,但它们调用的应用程序代码对我的 Factory 使用相同的 bean 定义。我不希望通过一长串设置器手动注入这个 bean。
  • 在我看来他是说你应该能够提供导致异常被抛出的输入,而不是使用存根。
  • @AHungerArtist:这没有多大意义。这个搜索引擎是一个外部 jar。我无法改变它的工作方式。没有我可以传递给 getDocuments() 的值可以预测地导致它抛出异常,所以有一个特定的值我传递给模拟来导致这种行为是没有意义的,不是吗?
  • @Samo - 如果没有办法可以预见到异常,那么您如何期望异常呢?抱歉,我没有看到完整的图片。
  • 重点不是模拟导致异常的场景。关键是要确保正确处理异常。如果我的引擎抛出异常,我想确保调用这个引擎的服务吃掉它并返回一个空列表。所以我只想引起异常,我不在乎如何。如果我正在测试不同的层,我可以将参数传递到 getDocumentsInRange(int, int) 中,这会导致异常。但我正在测试引擎上方的一层以确保它吃掉异常,所以我无法控制哪些参数被传递到方法中。
【解决方案2】:

注入搜索引擎的不同实现怎么样?只需创建更多代表搜索引擎不同模拟的 bean。

  • 一个测试类用一个模拟初始化,另一个测试类用另一个模拟初始化;当然,这意味着您将有更多的测试类用于您正在测试的某个类(不是那么好/干净)

或者...

  • 在 1 个测试类中注入更多模拟(搜索引擎的)。一些测试方法(来自该测试类)使用一个模拟,其他测试方法使用另一个模拟

【讨论】:

  • @Belun:您的第二个建议不起作用,因为引用我的 bean 的代码是应用程序代码,而不是测试代码。加载定义此 bean 的上下文是测试的责任。只有当我有多个测试应用程序上下文时,您的第一个建议才有可能。即使我这样做了,这听起来像是不好的做法,我也无法运行我的其他测试,因为他们会使用引发异常的 bean。正如你所建议的,我必须为这些设置多个测试类。
  • @Samo:听起来有些不对劲。到目前为止,我看到了三个用于搜索引擎的 bean 和 2 个应用程序 xml。真正的实现是一个,位于用于生产的应用程序 xml 中。其他 2 个 bean 是一个返回一些固定值(存根)和一个在特定情况下抛出一些异常的 bean;最后两个都在第二个应用程序 xml 中(用于测试)。我的照片怎么了?
  • @Belun:你的图片有问题的是一段应用程序代码不能引用来自同一上下文的两个不同的bean。正如我所说,我的 bean 不是从测试代码加载的,它在应用程序代码中被引用。例如,我的测试调用 A 类。A 类从应用程序上下文中引用 bean X。我没有设置器来更改这个 bean,我也不想要任何。因此 bean X 采用在当前应用程序上下文中找到的任何定义。这更有意义吗?
  • @Samo 在您的测试类中为 A 类(正在测试的类)创建两个 bean。一个 bean 注入了一个 mock,而另一个 bean 注入了另一个 mock。它们是 2 个不同的 bean,碰巧属于同一类型(A 类)并且用于不同的测试方法
  • @Belun:我不确定我在这里是如何不清楚的。 Beans 在我的测试代码中没有被引用! 它们在应用程序代码中被引用,我的测试代码调用了它。在我的测试代码中引用一个 bean 一点用都没有。 A 类引用 bean。实际上,它更像是,测试调用类A,调用类B,调用类C,引用bean。我没有 bean 的设置器。我不想要 bean 的设置器。如果我有任何打算这样做,我就不会发布这个问题。我不想要一些超长的设置器链,只是为了测试我的代码。
【解决方案3】:

代替:

factory = EngineMocks.createMockEngineFactory();

做:

factory.callMethodThatChangesTheStateOfThisObjectSuchThatItIsSuitableForYourTest(withOptionalParameters);

另外,如果您使用的是 Spring 集成测试,请确保使用 @DirtiesContext 注释您的方法,以免影响下一次测试。

【讨论】:

    猜你喜欢
    • 2018-12-15
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多