gpt4 book ai didi

java - Mockito、JUnit 和 Spring

转载 作者:IT老高 更新时间:2023-10-28 11:49:47 34 4
gpt4 key购买 nike

我今天才开始了解 Mockito。我写了一些简单的测试(使用 JUnit,见下文),但我不知道如何在 Spring 的托管 bean 中使用模拟对象。什么是使用 Spring 的最佳实践。我应该如何向我的 bean 注入(inject)模拟依赖项?

你可以跳过这个直到回到我的问题

首先,我学到了什么。这是一篇很好的文章Mocks Aren't Stubs这解释了基础知识(Mock 的检查行为验证而不是状态验证)。那么这里有一个很好的例子Mockito 在这里Easier mocking with mockito .我们解释说 Mockito 的模拟对象既是 mock 又是 stub

这里 Mockito在这里Matchers ,您可以找到更多示例。

这个测试

@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());
}

工作得很好。

回到我的问题。 这里 Injecting Mockito mocks into a Spring bean有人尝试使用 Springs ReflectionTestUtils.setField(),但这里是 Spring Integration Tests, Creating Mock Objects我们建议改变 Spring 的上下文。

我并没有真正理解最后两个链接...有人可以向我解释一下 Spring 对 Mockito 有什么问题吗?这个解决方案有什么问题?

@InjectMocks
private MyTestObject testObject

@Mock
private MyDependentObject mockedObject

@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}

https://stackoverflow.com/a/8742745/1137529

编辑:我不是很清楚。我将提供 3 个代码示例来澄清我自己:假设,我们有带有 printHello() 方法的 bean HelloWorld 和带有 sayHello 方法的 bean HelloFacade,它们将调用转发到 HelloWorld 的方法 printHello()

第一个例子是使用 Spring 的上下文并且没有自定义运行器,使用 ReflectionTestUtils 进行依赖注入(inject) (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();
}

}

正如@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 是接口(interface)。在最后一个示例中,我们可以只声明 HelloFacadeImpl,Mokito 将为我们实例化它。这种方法的缺点是,现在被测单元是 impl-class 而不是接口(interface)。

最佳答案

老实说,我不确定我是否真的理解你的问题:P 我会尽量澄清,从你原来的问题中得到的:

首先,在大多数情况下,您不应该对 Spring 有任何顾虑。您很少需要让 spring 参与编写单元测试。一般情况下,你只需要在你的单元测试中实例化被测系统(SUT,被测目标),并在测试中也注入(inject)SUT的依赖。依赖项通常是一个模拟/ stub 。

您最初建议的方式,示例 2、3 正是我上面描述的。

在极少数情况下(例如集成测试或一些特殊的单元测试),您需要创建一个 Spring 应用程序上下文,并从应用程序上下文中获取您的 SUT。在这种情况下,我相信您可以:

1) 在 spring app ctx 中创建您的 SUT,获取对其的引用,并向其注入(inject)模拟

@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 Integration Tests, Creating Mock Objects 中描述的方式进行操作.这种方法是在 Spring 的应用程序上下文中创建模拟,您可以从应用程序 ctx 中获取模拟对象来进行 stub /验证:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {

@Autowired
TestTarget sut;

@Autowired
Foo mockFoo;

@Test
public void someTest() {
// ....
}
}

这两种方法都应该有效。主要区别在于前一种情况将在经过spring的生命周期等之后注入(inject)依赖项(例如bean初始化),而后一种情况是预先注入(inject)的。例如,如果您的 SUT 实现了 spring 的 InitializingBean,并且初始化例程涉及依赖项,您将看到这两种方法之间的区别。我相信这两种方法没有对错之分,只要你知道自己在做什么。

只是一个补充,@Mock、@Inject、MocktoJunitRunner 等在使用 Mockito 时都是不必要的。它们只是实用程序,可帮助您节省键入 Mockito.mock(Foo.class) 和一堆 setter 调用的时间。

关于java - Mockito、JUnit 和 Spring,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10906945/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com