【问题标题】:spring 3 autowiring and junit testingspring 3 自动装配和junit测试
【发布时间】:2011-04-09 07:46:06
【问题描述】:

我的代码:

@Component
public class A {
    @Autowired
    private B b;

    public void method() {}
}

public interface X {...}

@Component
public class B implements X {
    ...
}

我想在 A 类隔离测试。我必须模拟 B 类吗?如果是,如何?因为它是自动装配的,并且没有设置器可以发送模拟对象。

【问题讨论】:

    标签: spring junit autowired


    【解决方案1】:

    我想在 A 类隔离测试。

    您绝对应该模拟 B,而不是实例化和注入 B 的实例。重点是测试 A 是否 B 工作,因此您不应该让可能损坏的 B 干扰 A 的测试。

    也就是说,我强烈推荐Mockito。随着模拟框架的发展,它非常易于使用。您可以编写如下内容:

    @Test
    public void testA() {
        A a = new A();
        B b = Mockito.mock(B.class); // create a mock of B
        Mockito.when(b.getMeaningOfLife()).thenReturn(42); // define mocked behavior of b
        ReflectionTestUtils.setField(a, "b", b); // inject b into the B attribute of A
    
        a.method();
    
        // call whatever asserts you need here
    }
    

    【讨论】:

    • 在新版本的 Mockito 中,我会在 A 的声明中使用 @InjectMocks 注释并摆脱 setField(..) 的反射
    • 但是 a 是一个 bean,即一个代理(使用 AOP 创建)。这不会成功。我尝试过类似的方法,错误是在 A 上找不到字段 b(因为它是代理)。
    • 我理解想要测试 A 类的隔离,但为什么 B 绝对要被嘲笑呢?如果 B 坏了,你就无法让 A 工作。
    • @dtc 这很好。自从我回答了这个问题以来,我对模拟对象的看法发生了变化,而这些天来,我首先会找到一种方法来将 A 与 B 分离。
    【解决方案2】:

    以下是我如何使用 Spring 3.1、JUnit 4.7 和 Mockito 1.9 进行测试的示例:

    FooService.java

    public class FooService {
        @Autowired private FooDAO fooDAO;
        public Foo find(Long id) {
            return fooDAO.findById(id);
        }
    }
    

    FooDAO.java

    public class FooDAO {
        public Foo findById(Long id) {
            /* implementation */
        }
    }
    

    FooServiceTest.java

    @RunWith(MockitoJUnitRunner.class)
    public class FooServiceTest {
        @Mock private FooDAO mockFooDAO;
        @InjectMocks private FooService fooService = new FooService();
    
        @Test public final void findAll() {
            Foo foo = new Foo(1L);
            when(mockFooDAO.findById(foo.getId()).thenReturn(foo);
    
            Foo found = fooService.findById(foo.getId());
            assertEquals(foo, found);
        }
    }
    

    【讨论】:

    • 如果不使用MockitoJUnitRunner,请记住FooServiceTest@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
    【解决方案3】:

    您可以使用 Spring 的 ReflectionTestUtils.setField(或 junit 扩展 PrivateAccessor)通过反射注入该字段,或者您可以创建一个模拟应用程序上下文并加载它。虽然对于简单的单元(非集成)测试,我更喜欢使用反射来简化。

    【讨论】:

      【解决方案4】:

      This forum discussion 对我来说很有意义。您可以将私有成员 b 声明为由类 B 实现的 InterfaceB 类型(即:面向服务),然后声明 MockB 类也将实现相同的接口。在您的测试环境应用程序上下文中,您声明 MockB 类,而您的生产应用程序上下文则声明普通 B 类,无论哪种情况,都不需要更改 A 类的代码,因为它会自动连接。

      【讨论】:

      • 使用@Autowire,您无需在应用程序上下文中声明您的bean,它们就在类路径中。所以,这个解决方案行不通。
      • 如果您按类型连接 bean(在 2.5 中)或声明限定符 (3.0),则此解决方案有效。在 Spring 2.5 中,您可以自动装配到 XML 应用程序上下文配置中声明的 bean,如可以在文档 static.springsource.org/spring/docs/2.5.x/reference/… 中阅读的那样。在 Spring 3.0 中,它们仍然允许您使用 @Qualifier 注释(记录在 static.springsource.org/spring/docs/3.0.x/…)来执行此操作。
      猜你喜欢
      • 2012-08-13
      • 2011-06-10
      • 1970-01-01
      • 2018-12-01
      • 2020-02-09
      • 1970-01-01
      • 1970-01-01
      • 2021-12-23
      • 2015-03-03
      相关资源
      最近更新 更多