ale*_*ail 75 java spring unit-testing mockito
我今天才开始了解Mockito.我写了一些简单的测试(使用JUnit,见下文),但我无法弄清楚如何在Spring的托管bean中使用模拟对象.使用Spring的最佳实践是什么?我应该如何向我的bean注入模拟依赖?
你可以跳过这个直到回到我的问题.
首先,我学到了什么.这是非常好的文章Mocks Are Not Stubs解释了基础知识(Mock的检查行为验证不是状态验证).然后有一个很好的例子Mockito 和这里更容易嘲笑mockito.我们有解释说Mockito的模拟对象都是模拟和存根.
这个测试
@Test
public void testReal(){
List<String> mockedList = mock(List.class);
//stubbing
//when(mockedList.get(0)).thenReturn("first");
mockedList.get(anyInt());
OngoingStubbing<String> stub= when(null);
stub.thenReturn("first");
//String res = mockedList.get(0);
//System.out.println(res);
//you can also verify using argument matcher
//verify(mockedList).get(anyInt());
verify(mockedList);
mockedList.get(anyInt());
}
Run Code Online (Sandbox Code Playgroud)
工作得很好.
回到我的问题.在这里注入Mockito模拟到Spring bean有人试图使用Springs ReflectionTestUtils.setField(),但是在这里Spring Integration Tests,创建模拟对象我们建议改变 Spring的上下文.
我真的不明白最后两个链接......有人可以向我解释一下Spring与Mockito有什么问题吗?这个解决方案有什么问题?
@InjectMocks
private MyTestObject testObject
@Mock
private MyDependentObject mockedObject
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
Run Code Online (Sandbox Code Playgroud)
编辑:我不是很清楚.我将提供3个代码示例以阐明我的自我:假设,我们有bean HelloWorld with method printHello()和bean HelloFacade with method sayHelloto forward调用HelloWorld的方法printHello().
第一个例子是使用Spring的上下文而没有自定义运行器,使用ReflectionTestUtils进行依赖注入(DI):
public class Hello1Test {
private ApplicationContext ctx;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}
@Test
public void testHelloFacade() {
HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
HelloWorld mock = mock(HelloWorld.class);
doNothing().when(mock).printHello();
ReflectionTestUtils.setField(obj, "hello", mock);
obj.sayHello();
verify(mock, times(1)).printHello();
}
}
Run Code Online (Sandbox Code Playgroud)
正如@Noam指出的那样,无需显式调用即可运行它MockitoAnnotations.initMocks(this);.我也将在这个例子中使用Spring的上下文.
@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {
@InjectMocks
private HelloFacade obj = new HelloFacadeImpl();
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
Run Code Online (Sandbox Code Playgroud)
另一种方法
public class Hello1aTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@InjectMocks
private HelloFacadeImpl obj;
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
Run Code Online (Sandbox Code Playgroud)
不,在那个例子中,我们必须手动实现HelloFacadeImpl并将其分配给HelloFacade,因为HelloFacade是接口.在最后一个例子中,我们可以声明HelloFacadeImpl,Mokito将为我们实例化它.这种方法的缺点是,现在,被测单元是impl-class而不是接口.
Adr*_*hum 53
老实说,我不确定我是否真的理解你的问题:PI将尽可能多地澄清我从原始问题中得到的结论:
首先,在大多数情况下,你不应该对Spring有任何担忧.你很少需要参与编写单元测试.在正常情况下,您只需要在单元测试中实例化被测系统(SUT,要测试的目标),并在测试中注入SUT的依赖关系.依赖项通常是mock/stub.
你原来建议的方式,例2,3,正是我上面所描述的.
在一些罕见的情况下(例如,集成测试或一些特殊的单元测试),您需要创建一个Spring应用程序上下文,并从应用程序上下文中获取您的SUT.在这种情况下,我相信你可以:
1)在spring app ctx中创建你的SUT,获得它的引用,并为它注入模拟
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {
@Autowired
@InjectMocks
TestTarget sut;
@Mock
Foo mockFoo;
@Before
/* Initialized mocks */
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void someTest() {
// ....
}
}
Run Code Online (Sandbox Code Playgroud)
要么
2)按照链接Spring Integration Tests,Creating Mock Objects中描述的方式进行操作.这种方法是在Spring的app环境中创建模拟,你可以从app ctx获取模拟对象来进行存根/验证:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {
@Autowired
TestTarget sut;
@Autowired
Foo mockFoo;
@Test
public void someTest() {
// ....
}
}
Run Code Online (Sandbox Code Playgroud)
两种方式都应该有效.主要区别在于前一种情况将在经历spring的生命周期等之后注入依赖项(例如bean初始化),而后一种情况在beforehands之前注入.例如,如果您的SUT实现了spring的InitializingBean,并且初始化例程涉及依赖项,您将看到这两种方法之间的区别.我相信这两种方法没有对错,只要你知道你在做什么.
只是补充,@ Mock,@ Inject,MocktoJunitRunner等在使用Mockito时都是不必要的.它们只是保存您键入Mockito.mock(Foo.class)和一组setter调用的实用工具.
您的问题似乎在于询问您提供的三个示例中的哪一个是首选方法.
使用Reflection TestUtils的示例1不是单元测试的好方法.您真的不想为单元测试加载弹簧上下文.只需模拟并注入其他示例所示的内容即可.
如果你想进行一些集成测试,你确实想要加载spring上下文,但是如果你需要显式访问它的bean ,我更愿意使用@RunWith(SpringJUnit4ClassRunner.class)上下文的加载@Autowired.
示例2是一种有效的方法,并且使用@RunWith(MockitoJUnitRunner.class)将删除指定@Before方法和显式调用的需要MockitoAnnotations.initMocks(this);
示例3是另一种不使用的有效方法@RunWith(...).您没有HelloFacadeImpl明确地在测试中实例化您的类,但您可以使用示例2完成相同的操作.
我的建议是使用示例2进行单元测试,因为它减少了代码混乱.如果您被迫这样做,您可以回退到更详细的配置.
| 归档时间: |
|
| 查看次数: |
104931 次 |
| 最近记录: |