0

I have a Controller Unit test using Mockito and MockMvc.

After a POST request, the POSTed object is resolved correctly, but my repository mock is not triggered.

Here is the mock code:

    Date mydate = new Date();
    Notification not = new Notification();
    not.setId(-1L);
    not.setUserid("BaBlubb");
    not.setTimestamp(mydate);
    not.setContent("MyContent");

    Notification not2 = new Notification();
    not2.setId(1L);
    not2.setUserid("BaBlubb");
    not2.setTimestamp(mydate);
    not2.setContent("MyContent");

    when(notificationRepository.save(not)).thenReturn(not2);

So this really just should simulate the saving of an object (ID is set and a route is generated out of it).

Unfortunately, the repository always returns null, so my code later fails when trying to return a new created Route out of null.

The mocks are correctly injected and do work for e.g. String comparison or if I only check for the function to have been called, I just can't get it to trigger on Objects.

The same issue occurs on

        verify(notificationRepository, times(1)).save(not);

it does not trigger.

The questions would be: 1.) Why does the mock not trigger? I suppose it does not check for value equality in the object, but for object identifiers, which are not the same since the object is serialized and de-serialized in between.

2.) How can I get a generic mock? e.g. whenever repository.save() is called, no matter the parameter, it always should perform a specific way, e.g. instead of

when(notificationRepository.save(not)).thenReturn(not2);

I'd like to have

when(notificationRepository.save()).thenReturn(not2);

P.S. If for whatever reason you need the rest of the code, here is the submitted part, object is the json representation of the notification (with jackson)

 mockMvc.perform(post("/api/notification").content(object)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON));

and here is the Controller header, the Object is de-serialized perfectly, the values are 1:1 the same

 @RequestMapping(method=RequestMethod.POST)
    public ResponseEntity<?> postNotification(@RequestBody Notification n) {
        logger.debug("Saving userid "+n.getId());

Thanks for helping.

4

2 回答 2

1

1.) 为什么模拟没有触发?我想它不检查对象中的值是否相等,而是检查对象标识符,它们不一样......

默认情况下,Mockito 委托给您的对象的equals方法。如果您没有覆盖它,则默认情况下它会检查引用。以下两行是等价的:

when(notificationRepository.save(not)).thenReturn(not2);
when(notificationRepository.save(not)).thenReturn(eq(not2)); // uses eq explicitly

如果具有相同字段的所有 Notification 对象都相同,则覆盖equals并将hashCode您带到您需要去的地方。但是请注意,这可能会对 Set 和 Map 行为产生意想不到的副作用,特别是如果您的 Notification 对象在保存之前没有 ID。

2.) 我怎样才能得到一个通用的模拟?例如,无论何时调用 repository.save(),无论参数如何,它总是应该执行特定的方式

使用 Matchers,这非常简单:

when(notificationRepository.save(not)).thenReturn(any(Notification.class));

尽管 Matcher 非常强大,但请注意:它们的使用有一些棘手的规则

于 2015-06-12T17:55:17.593 回答
1

对于 (1),正如 Jeff 所提到的,您可能需要使用 eq() 而不是直接引用not1
For (2) 您可以使用Mockito.any()
For 例如这将在模拟对象notificationRepositorywhen(notificationRepository.save(any(Notification.class))).thenReturn(not2);
上创建存根,该存根将始终返回任何参数类型。如果方法接受 Object 那么你可以写which 将返回任何类型的参数not2Notificationsave()when(notificationRepository.save(any(Object.class))).thenReturn(not2);not2Object

于 2015-06-12T20:00:09.657 回答