当前位置: 首页 > 面试题库 >

Mockito,JUnit和Spring

空枫涟
2023-03-14
问题内容

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

你可以跳过这一步,直到回到我的问题。

首先,我学到了什么。这是一篇很好的文章Mocks Are n’t 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());
}

效果很好。

回到我的问题。这里有人将Mockito模拟注入到Spring Bean中,有人尝试使用Springs ReflectionTestUtils.setField(),但是这里我们建议通过Spring Integration Tests,创建Mock对象来更改 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个代码示例来阐明自己:假设,我们有带方法bean HelloWorld和带方法的printHello()bean HelloFacade,sayHello它们将对呼叫的调用转发到HelloWorldmethod printHello()

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

}

没什么,在前面的示例中,因为HelloFacade是接口,所以我们必须手动实例化HelloFacadeImpl并将其分配给HelloFacade。在最后一个示例中,我们只需要声明HelloFacadeImpl即可,Mokito将为我们实例化它。这种方法的缺点是,现在,被测单元是impl类,而不是接口。


问题答案:

老实说,我不确定我是否真的理解你的问题:PI将尽力从你最初的问题中得到的澄清:

首先,在大多数情况下,你对Spring不应有任何担心。编写单元测试时几乎不需要弹簧。在正常情况下,你只需要在单元测试中实例化被测系统(SUT,要测试的目标),并在测试中注入SUT的依赖项。依赖关系通常是模拟/存根。

你最初建议的方式以及示例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() {
         // ....
    }
}

要么

2)按照链接“ Spring Integration Tests,创建模拟对象”中所述的方法进行操作。这种方法是在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等在使用Mockito时都是不必要的。它们只是节省你键入Mockito.mock(Foo.class)和一堆setter调用的实用程序。



 类似资料:
  • 你可以跳过这个直到回到我的问题。 首先,我学到了什么。这是一篇很好的文章,它解释了基础(mock的检查、行为验证,而不是状态验证)。然后有一个很好的例子,这里是Mockito,这里是Mockito更容易的嘲弄。我们解释了mockito的mock对象既是mock又是stub。 在这里Mockito和这里Matchers,你可以找到更多的例子。 我不太明白最后两个环节...有人能给我解释一下Sprin

  • 我正在使用Mockito编写一个JUnit测试用例,并得到一个NullPointerException。 在我的代码中是这样的:

  • 我在我的Java,Spring Boot控制器中创建了一个函数,它允许我根据参数获得数据的和值,这很有效。然而,我很难理解用Junit和Mockito测试这个功能的最佳方式是什么?到目前为止,我已经创建了一个测试函数,它返回一个特定数组字段的值。如何能够返回。thenreturn()中的值,该值根据给定的serviceID求和?任何帮助或建议任何其他有用的帖子将被感谢,因为我无法找到任何相关的或我

  • 为了在我的spring boot项目上运行单元测试,我尝试使用JUnit和Mockito,但是很难找到如何在我的gradle项目的build.gradle文件中设置依赖项。下面是build.gradle文件: 现在我得到的错误是“不能解析org.Mockito:mockito-core:3.6.48”,我甚至不知道是否从https://github.com/Mockito/Mockito/rele

  • 我应该如何测试异常?我可以mock connector并且我可以赋予它抛出异常的行为,但是我不明白下一步该怎么做。

  • 我有这个过滤器类,在使用junit进行测试时需要尽可能高的代码覆盖率。 和测试等级: 当我运行时,它在 线 我如何避免这种情况? 我需要调用这个方法并执行里面的任何内容来提供所需的代码覆盖。