Mockito验证单元测试 - 通缉但未调用。实际上,与这个模拟没有任何交互

时间:2017-09-27 14:29:50

标签: java unit-testing amazon-web-services mockito verify

起初我想抱歉我的英语。

我开始进行一些单元测试(我以前从未这样做过,我是编程中的新人)。

我必须使用mockito.verify测试简单地将产品添加到数据库(DynamoDB)方法,但我有

"Wanted but not invoked. Actually, there were zero interactions with this mock." 

错误,我不知道该怎么做。

这是我的方法代码(在KitchenService类中):

public Product addProduct(Product content) {

    ObjectMapper objectMapper = new ObjectMapper();

    String mediaJSON = null;
    String authorJSON = null;
    String productKindsJSON = null;
    try {
        mediaJSON = objectMapper.writeValueAsString(content.getMedia());
        authorJSON = objectMapper.writeValueAsString(content.getAuthor());
        productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
    } catch (JsonProcessingException e) {
        logger.log(e.getMessage());
    }


    Item item = new Item()
            .withPrimaryKey("id", UUID.randomUUID().toString())
            .with("name", content.getName())
            .with("calories", content.getCalories())
            .with("fat", content.getFat())
            .with("carbo", content.getCarbo())
            .with("protein", content.getProtein())
            .with("productKinds", productKindsJSON)
            .with("author", authorJSON)
            .with("media", mediaJSON)
            .with("approved", content.getApproved());


    Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
    logger.log(save + " created");



    return content;

}

这是测试代码:

@Test
public void addProduct() throws Exception {


    KitchenService instance = mock(KitchenService.class);


    Product expectedProduct = new Product();
    expectedProduct.setName("kaszanka");
    expectedProduct.setCalories(1000);
    expectedProduct.setFat(40.00);
    expectedProduct.setCarbo(20.00);
    expectedProduct.setProtein(40.00);
    expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
    expectedProduct.setApproved(false);
    Author expectedAuthor = new Author();
    expectedAuthor.setId("testID");
    expectedAuthor.setName("Endrju Golota");
    expectedProduct.setAuthor(expectedAuthor);
    Media expectedMedia = new Media();
    expectedMedia.setMediaType(MediaType.IMAGE);
    expectedMedia.setName("dupajasia");
    expectedMedia.setUrl("http://blabla.pl");
    expectedProduct.setMedia(expectedMedia);

    verify(instance, times(1)).addProduct(expectedProduct);
}

这是我在测试后得到的:

Wanted but not invoked:
kitchenService.addProduct(
    model.kitchen.Product@a0136253
);
-> at     service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.

有人可以告诉我,我做错了吗?

3 个答案:

答案 0 :(得分:0)

您应该模拟和验证的是IO[(A, B)]依赖项:

databaseController

您应该验证是否在服务中调用了数据库..检查它是否与任何@Test public void addProduct() throws Exception { KitchenService instance = new KitchenService(); // you should create the class under test DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller instance.setController(controller); // inject the mock ... // Act instance.addProduct(expectedProduct); // Assert verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class)); } 对象一起调用就足够了。

答案 1 :(得分:0)

Mocking是一种工具,您只能用于正在测试的类的依赖项。 您的测试似乎并不关心作者,媒体和产品对象, 这些只是您要测试的方法的依赖项; 嘲笑他们。

组织将极大地帮助您进行测试; 做这样的事情:

public class TestKitchenService
{
    private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
    ... use constants for other values as well.  The value of the constant does not matter.

    @InjectMocks
    private KitchenService classToTest;

    private InOrder inOrder;

    @Mock
    private Author mockAuthor;

    @Mock
    private DatabaseController mockDatabaseController;

    @Mock
    private Logger mockLogger;

    @Mock
    private Media mockMedia;

    @Mock
    private Product mockProduct;

    @After
    public void afterTest()
    {
        inOrder.verifyNoMoreInteractions();

        verifyNoMoreInteractions(mockAuthor);
        verifyNoMoreInteractions(mockDatabaseController);
        verifyNoMoreInteractions(mockLogger);
        verifyNoMoreInteractions(mockMedia);
        verifyNoMoreInteractions(mockProduct);
    }

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

        doReturn(mockAuthor).when(mockProduct).getAuthor();
        doReturn(mockMedia).when(mockProduct).getMedia();
        doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
        doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();

        ... doReturns for the other product values.

        inOrder = inOrder(
            mockAuthor,
            mockDatabaseController,
            mockLogger,
            mockMedia,
            mockProduct);

        ReflectionTestUtils.setField(
            classToTest,
            "databaseController",
            mockDatabaseController);

        ReflectionTestUtils.setField(
            classToTest,
            "logger",
            mockLogger);
    }

    @Test
    public void addProduct_success()
    {
        final Product actualResult;


        actualResult = classToTest.addProduct(mockProduct);


        assertEquals(
            mockProduct,
            actualResult);

        inOrder.verify(mockProduct).getMedia();

        inOrder.verify(mockProduct).getAuthor();

        inOrder.verify(mockProduct).getProductKinds();

        inOrder.verify(mockProduct).getName();

        ... inOrder.verify for the other product values.

        inOrder.verify(mockDatabaseController).saveProduct(
            eq(PRODUCT_TABLE),
            any(Item.class));
    }
}

答案 2 :(得分:0)

唯一应该被模拟的东西 - 如果有的话 - 是ObjectMapper和databaseController。一个人只能嘲笑协作者对象,而且几乎从不测试被测系统/类(在SUT上“间谍”的情况非常罕见)。并且取决于ObjectMapper是什么以及它的操作是多么透明,你可能真的不想甚至嘲笑它。此外,由于您的实现代码是通过直接调用构造函数来实例化ObjectMapper而编写的,因此您甚至无法模拟它。

虽然我喜欢使用Mockito和模拟对象,但有时候用尽可能多的真实对象进行测试是值得的。当您的协作者简单,直接,没有副作用,并且不需要复杂的初始化或设置时,尤其如此。只有在简化测试设置或验证时才使用模拟。