【问题标题】:Request scope bean management in Unit Tests单元测试中的请求范围 bean 管理
【发布时间】:2012-04-17 15:23:10
【问题描述】:

在单元测试中有很多关于请求范围管理的问题,主要答案是不要测试范围管理,因为它是 Spring Framework 任务,应该注意它是否正常工作。因此,建议例如将请求范围替换为 XML 配置文件中的线程或原型类型范围。

对于大多数测试来说已经足够了,没有关于未注册“请求”范围的抱怨,并且测试运行良好。但我确实有一种情况是不够的。

考虑以下情况:

@Component
@Scope("request")
public class MyService {

    @Autowired
    private MyComponent component;

    public void doSomething(String param) {
        component.doTheThing(param);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:my-scope-tweaks.xml"})
public class MyServiceTest {

    @Autowired
    private MyService service;

    @Autowired
    private MyComponent component;

    @Test
    public void test1() {
        service.doSomething("aaa");

        assertEquals("AAA", component.getTheThing());
    }

    @Test
    public void test1() {
        service.doSomething("bbb");

        assertEquals("BBB", component.getTheThing());
    }

}

我想测试请求范围内的 MyService。 MyComponent 也是请求范围。

变体 A

如果我用SimpleThreadScope 替换请求范围,那么在两个测试中我将收到MyServiceMyComponent 的相同实例,例如test2() 可能会收到来自MyComponent 的错误结果内部可能包含一些来自之前test1()的内部“垃圾”

变体 B

如果我用原型范围替换请求范围 - 我会遇到我的测试方法接收到 MyComponent 的不同实例的情况,就像 MyService 那样 - 所以我无法对它们执行任何断言。

所以我需要的是一种与测试方法相关的请求范围,其中所有请求范围的 bean 仅在 test1() 方法期间保留,然后被销毁,因此在下一个 test2() 中它们将被重新创建。

有可能吗?

【问题讨论】:

    标签: java spring unit-testing junit scope


    【解决方案1】:

    这可能不是您想要的,但您为什么首先使用 Spring 来管理您的测试类?这对我来说似乎有点矫枉过正。对于单元测试,您不需要 DI 容器。只需模拟依赖项并专注于您正在测试的组件或服务的封装功能。

    但是,在使用字段注入时,您将无法做到这一点。您需要转换为构造函数或方法注入。

    【讨论】:

    • 我目前正在做的这种集成测试。对于小型单元测试,我不使用 DI,但在这种情况下 - 它更大并且出现了许多注入(示例被简化以更好地解释) - 所以手动重做所有工作就像 Spring 通常所做的那样是矫枉过正的......跨度>
    • 我明白你现在在说什么了。但是,您注入Component 的方式仍然存在代码异味。您希望 Spring 为每个测试再次重新注入它,但这不会发生,即使使用原型范围(变体 B)也是如此。 Spring 只会注入一次对象。为了做你想做的事,你必须每次都为你需要的对象准备一份Contextask Spring 的副本。另外,您是否可以通过MyService 对象访问您需要断言的状态,而不是需要Component 的单独副本?
    • 你说得对,使用原型通过MyService 访问MyComponent 可以为我做这件事,所以我不需要在我的测试类中另外添加@Autowire
    • @Ryan 您可以使用 javax.inject.Provider 来解决这个问题。例如:link
    【解决方案2】:

    通过使用@DirtiesContext 注释方法,可以为单元测试中的每个方法接收新的上下文。这允许您在应用程序上下文中为一个单元测试操作 bean,而不会影响其他单元测试。

    Documentation

    你的例子用@DirtiesContext注释:

    @Component
    @Scope("request")
    public class MyService {
    
        @Autowired
        private MyComponent component;
    
        public void doSomething(String param) {
            component.doTheThing(param);
        }
    }
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:my-scope-tweaks.xml"})
    public class MyServiceTest {
    
        @Autowired
        private MyService service;
    
        @Autowired
        private MyComponent component;
    
        @Test
        @DirtiesContext
        public void test1() {
            service.doSomething("aaa");
    
            assertEquals("AAA", component.getTheThing());
        }
    
        @Test
        @DirtiesContext
        public void test2() {
            service.doSomething("bbb");
    
            assertEquals("BBB", component.getTheThing());
        }
    
    }
    

    如果你的类中的所有测试方法都需要一个新的上下文,你也可以只注释类,如下所示:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:my-scope-tweaks.xml"})
    @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
    public class MyServiceTest {
       //Tests Here...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 2013-01-21
      • 2011-11-11
      • 2020-12-02
      • 2023-03-05
      • 1970-01-01
      • 2012-05-26
      相关资源
      最近更新 更多