英文:
How to correctly mock an ObjectMapper object
问题
我在模拟一个com.fasterxml.jackson.databind.ObjectMapper
时遇到了困难。
特别是我想要测试的类如下:
@Service
public class ClassToBeTested {
private final ObjectMapper mapper;
public ClassToBeTested(ObjectMapper mapper) {
this.mapper = mapper;
}
public void publicMethod(messageDto dto) throws JsonProcessingException {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// do stuff
}
}
private void privateMethod(Object dto, String param, String param2) {
Map<String, Object> attributes = new HashMap<>();
attributes.putAll(mapper.readValue(mapper.writeValueAsString(dto),
new TypeReference<Map<String, Object>>() {
}));
attributes.put("level", "error");
//other stuff
}
}
我的测试类如下:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class ClassToBeTestedTest {
@MockBean
private ObjectMapper mapper;
private ClassToBeTested classToBeTestedActor;
@PostConstruct
public void initMock() throws JsonProcessingException {
when(mapper.writeValueAsString(any())).thenReturn(any());
when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String, Object>>>any()))
.thenReturn(new HashMap<>());
}
@Before
public void setup() {
classToBeTestedActor = new ClassToBeTested(mapper);
}
@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
// Given
final Dto dto = mock(Dto.class);
// When
when(mapper.writeValueAsString(any()))
.thenThrow(JsonProcessingException.class);
// Then
Assertions.assertThrows(JsonProcessingException.class,
() -> classToBeTestedActor.publicMethod(dto));
}
}
其目的是触发和测试JsonProcessingException
。测试失败是因为出现了InvalidUseOfMatchersException
:
Invalid use of argument matchers!
2 matchers expected, 3 recorded:
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:47)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
其中第 46 行和第 47 行分别是:
when(mapper.writeValueAsString(any())).thenReturn(any());
when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String, Object>>>any()))
.thenReturn(new HashMap<>());
我觉得这对我来说没有任何意义,因为我认为我没有混合使用匹配器和原始值。
我如何正确地模拟ObjectMapper
,以便我可以测试JsonProcessingException
?
英文:
I'm having a hard time mocking a com.fasterxml.jackson.databind.ObjectMapper
.
In particular this the class I want to test:
@Service
public class ClassToBeTested {
private final ObjectMapper mapper;
public ClassToBeTested(ObjectMapper mapper) {
this.mapper = mapper;
}
public void publicMethod(messageDto dto) throws JsonProcessingException {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// do stuff
}
}
private void privateMethod(Object dto, String param, String param2) {
Map<String, Object> attributes = new HashMap<>();
attributes.putAll(mapper.readValue(mapper.writeValueAsString(dto),
new TypeReference<Map<String, Object>>() {
}));
attributes.put("level", "error");
//other stuff
}
}
My test class:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class ClassToBeTestedTest {
@MockBean
private ObjectMapper mapper;
private ClassToBeTested classToBeTestedActor;
@PostConstruct
public void initMock() throws JsonProcessingException {
when(mapper.writeValueAsString(any())).thenReturn(any());
when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String,
Object>>>any())).thenReturn(new HashMap<>());
}
@Before
public void setup() {
classToBeTestedActor = new ClassToBeTested(mapper);
}
@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
// Given
final Dto dto = mock(Dto.class);
// When
when(mapper.writeValueAsString(any()))
.thenThrow(JsonProcessingException.class);
// Then
Assertions.assertThrows(JsonProcessingException.class,
() -> classToBeTestedActor.publicMethod(dto));
}
}
The purpose is to trigger and test the JsonProcessingException
. The test fails because of an InvalidUseOfMatchersException
:
Invalid use of argument matchers!
2 matchers expected, 3 recorded:
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:47)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
where line 46 and 47 respectively are:
when(mapper.writeValueAsString(any())).thenReturn(any());
when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String, Object>>>any()))
.thenReturn(new HashMap<>());
which makes no sense to me since I think I didn't mixed matchers and raw values.
How can I mock the ObjectMapper
correctly so that I can test JsonProcessingException
?
答案1
得分: 1
我不明白编写一个模拟对象映射器抛出异常并测试异常是否被抛出的原因。相反,测试应该针对您编写的自定义逻辑。如果您有这样的方法 -
public void publicMethod(messageDto dto) {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// 做一些事情
dto.setXXX("XXX"); // 测试应该测试这个
}
}
因此,测试应该测试dto是否被正确设置。
@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
// 给定
final Dto dto = new Dto();
// 当
when(mapper.writeValueAsString(any()))
.thenThrow(JsonProcessingException.class);
// 调用要测试的方法
classToBeTestedActor.publicMethod(dto);
// 测试是否调用了预期的模拟方法
verify(mapper, times(1)).writeValueAsString(any());
verifyNoMoreInteractions(mapper);
// 断言结果
Assert.assertEquals("XXX", dto.getXXX());
}
我理解你的观点,我有点同意。我只是想知道为什么,即使我强制映射器抛出异常,我仍然无法断言它
我认为这是因为异常被您捕获了。这就是为什么异常未传播到您的测试的原因。
public void publicMethod(messageDto dto) throws JsonProcessingException {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// 做一些事情
}
}
尝试将其更改为 -
public void publicMethod(messageDto dto) throws JsonProcessingException {
privateMethod(dto, param, "other string");
}
英文:
I don't see the reason behind writing a test which mocks Object Mapper to throw some exception and then test whether the exception was thrown or not. Instead the test should be for the custom logic which is developed by you. If you have a method like this -
public void publicMethod(messageDto dto) {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// do stuff
dto.setXXX("XXX"); // The test should be testing this
}
}
So, the test should be testing whether the dto is set correctly.
@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
// Given
final Dto dto = new Dto();
// When
when(mapper.writeValueAsString(any()))
.thenThrow(JsonProcessingException.class);
// Invoke the method to be tested
classToBeTestedActor.publicMethod(dto);
// Test whether expected mock methods are invoked
verify(mapper, times(1)).writeValueAsString(any());
verifyNoMoreInteractions(mapper);
// Assert results
Assert.assertEquals("XXX", dto.getXXX());
}
> I get your point and I kind of agree with that. I just want to
> understand why, even if I am forcing the mapper to throw that
> exception, I am unable to assert it
I think the reason for this is that the exception is catched by you. That is the reason, that exception isn't propagated to your test.
public void publicMethod(messageDto dto) throws JsonProcessingException {
try{
privateMethod(dto, param, "other string");
} catch(JsonProcessingException e){
// do stuff
}
}
Try changing this to -
public void publicMethod(messageDto dto) throws JsonProcessingException {
privateMethod(dto, param, "other string");
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论