Java 莫基托、朱尼特和春天
直到今天我才开始了解莫基托。我编写了一些简单的测试(使用JUnit,见下文),但我不知道如何在Spring的托管bean中使用模拟对象。使用Spring的最佳实践是什么。我应该如何向我的bean注入模拟依赖项 你可以跳过这个直到回到我的问题 首先,我学到了什么。 这是一篇很好的文章,它解释了基础知识(Mock的检查行为验证而不是状态验证)。这里有一个很好的例子 在这里。我们有一个解释,Mockito的mock对象是mock和stub 在这里和这里,你可以找到更多的例子 这个测试Java 莫基托、朱尼特和春天,java,spring,unit-testing,mockito,Java,Spring,Unit Testing,Mockito,直到今天我才开始了解莫基托。我编写了一些简单的测试(使用JUnit,见下文),但我不知道如何在Spring的托管bean中使用模拟对象。使用Spring的最佳实践是什么。我应该如何向我的bean注入模拟依赖项 你可以跳过这个直到回到我的问题 首先,我学到了什么。 这是一篇很好的文章,它解释了基础知识(Mock的检查行为验证而不是状态验证)。这里有一个很好的例子 在这里。我们有一个解释,Mockito的mock对象是mock和stub 在这里和这里,你可以找到更多的例子 这个测试 @Test pu
@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());
}
编辑:我不是很清楚。我将提供3个代码示例来阐明我的观点:
假设我们有方法为printHello()
的bean HelloWorld和方法为sayHello
的bean HelloFade,它们将调用转发到HelloWorld的方法printHello()
第一个示例是使用Spring的上下文,而不使用自定义运行程序,使用ReflectionTestUtils进行依赖项注入(DI):
正如@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();
}
}
另一种方法
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();
}
}
不,在以前的示例中,我们必须手动实例化HelloFacadeImpl并将其分配给HelloFacade,因为HelloFacade是接口。在最后一个示例中,我们可以声明HelloFacadeImpl,Mokito将为我们实例化它。这种方法的缺点是,现在测试的单元是impl类而不是接口。您实际上不需要MockitoAnnotations.initMocks(this)代码>如果您使用的是mockito 1.9(或更新版本)-您只需要以下内容:
@InjectMocks
private MyTestObject testObject;
@Mock
private MyDependentObject mockedObject;
@InjectMocks
注释将把所有的mock注入MyTestObject
对象。是否必须实例化@InjectMocks
注释字段的区别在于Mockito的版本,而不是使用MockitoJunitRunner还是MockitoAnnotations.initMocks
。在1.9中,它还将处理@Mock
字段的一些构造函数注入,它将为您进行实例化。在早期版本中,您必须自己实例化它
这就是我如何对SpringBean进行单元测试的方法。没问题。当人们想要使用Spring配置文件来实际执行模拟的注入时,他们会感到困惑,这是单元测试和集成测试的交叉点
当然,被测试的单元是一个Impl。你需要测试一个真正具体的东西,对吗?即使您将其声明为接口,您也必须实例化真实的对象来测试它。现在,您可以进入spies,它是围绕真实对象的存根/模拟包装器,但这应该适用于角落案例。您的问题似乎是,您给出的三个示例中,哪一个是首选方法
示例1使用反射TestUtils不是单元测试的好方法。您根本不想为单元测试加载spring上下文。只需模拟并注入所需的内容,如其他示例所示
如果您想进行一些集成测试,您确实希望加载spring上下文,但是如果您需要显式访问其“bean”,我更希望使用@RunWith(SpringJUnit4ClassRunner.class)
来执行上下文加载以及@Autowired
示例2是一种有效的方法,使用@RunWith(MockitoJUnitRunner.class)
将无需指定@Before方法和显式调用MockitoAnnotations.initMocks(this)代码>
示例3是另一种不使用@RunWith(…)
的有效方法。您没有显式地实例化测试中的类HelloFacadeImpl
,但是您可以对示例2执行同样的操作
我的建议是使用示例2进行单元测试,因为它减少了代码混乱。如果您被迫这样做,您可以退回到更详细的配置。老实说,我不确定我是否真的理解您的问题:p我将尽可能从您最初的问题中得到澄清:
首先,在大多数情况下,您不应该担心Spring。您很少需要让spring参与编写单元测试。在正常情况下,您只需要在单元测试中实例化被测试的系统(SUT,要测试的目标),并在测试中注入SUT的依赖项。依赖项通常是模拟/存根
您最初建议的方法,以及示例2、3,正是在做我上面描述的事情
在一些罕见的情况下(如集成测试或一些特殊的单元测试),您需要创建一个Spring应用程序上下文,并从应用程序上下文获取SUT。在这种情况下,我相信您可以:
1) 在spring应用程序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() {
// ....
}
}
或
2) 按照链接中描述的方式操作。这种方法是在Spring的应用程序上下文中创建模拟,您可以从应用程序ctx获取模拟对象来执行存根/验证:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {
@Autowired
TestTarget sut;
@Autowired
Foo mockFoo;
@Test
public void someTest() {
// ....
}
}
这两种方法都应该奏效。主要区别在于前者在经历spring的生命周期后(例如bean初始化)将注入依赖项,而后者则在之前注入。例如,如果您的SUT实现了spring的InitializingBean,并且初始化例程涉及依赖项,那么您将看到这两种方法之间的差异。我相信这两种方法没有对错之分,只要你知道自己在做什么
只是一个补充,@Mock、@Inject、MocktoJunitRunner等在我们身上都是不必要的
@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() {
// ....
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {
@Autowired
TestTarget sut;
@Autowired
Foo mockFoo;
@Test
public void someTest() {
// ....
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("yourTestApplicationContext.xml")
@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Autowired
@InjectMocks
TestTarget sut;
@Mock
Foo mockFoo;
@Test
public void someTest() {
// ....
}
}