gpt4 book ai didi

java - 如何使用 Mockito 部分模拟抛出异常的方法?

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:59:53 27 4
gpt4 key购买 nike

测试异常处理很有用。在这种特定情况下,我有一个提取器,它会在解码特定类时抛出异常时执行特定任务。

示例代码

下面是代码的简化示例。生产版本要复杂得多。

public class Example {
public static enum EntryType {
TYPE_1,
TYPE_2
}

public static class Thing {
List<String> data = new ArrayList<String>();
EnumSet<EntryType> failedConversions = EnumSet.noneOf(EntryType.class);
}

public static class MyHelper {
public String unmarshal(String input) throws UnmarshalException {
// pretend this does more complicated stuff
return input + " foo ";
}
}

public static class MyService {

MyHelper adapter = new MyHelper();

public Thing process() {
Thing processed = new Thing();

try {
adapter.unmarshal("Type 1");
} catch (UnmarshalException e) {
processed.failedConversions.add(EntryType.TYPE_1);
}

// do some stuff

try {
adapter.unmarshal("Type 2");
} catch (UnmarshalException e) {
processed.failedConversions.add(EntryType.TYPE_2);
}

return processed;
}
}
}

我尝试过的事情

这是我尝试过的事情的列表。为简洁起见,我没有填写所有日常细节。

spy

下面的方法什么都不做,也没有抛出异常。我不确定为什么。

@Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {
MyHelper spied = spy(fixture.adapter);
doThrow(new UnmarshalException("foo")).when(spied).unmarshal(
Mockito.eq("Type 1"));

Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_1), is(true));
}

模拟

以下内容不起作用,因为部分模拟似乎不能很好地处理抛出异常的方法。

@Test
public void shouldFlagFailedConversionUsingMocks()
throws Exception {
MyHelper mockAdapter = mock(MyHelper.class);
when(mockAdapter.unmarshal(Mockito.anyString())).thenCallRealMethod();
when(mockAdapter.unmarshal(Mockito.eq("Type 2"))).thenThrow(
new UnmarshalException("foo"));

Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_2), is(true));
}

然后回答

这行得通,但我不确定这样做是否正确:

@Test
public void shouldFlagFailedConversionUsingThenAnswer() throws Exception {
final MyHelper realAdapter = new MyHelper();
MyHelper mockAdapter = mock(MyHelper.class);
fixture.adapter = mockAdapter;

when(mockAdapter.unmarshal(Mockito.anyString())).then(
new Answer<String>() {

@Override
public String answer(InvocationOnMock invocation)
throws Throwable {
Object[] args = invocation.getArguments();
String input = (String) args[0];
if (input.equals("Type 1")) {
throw new UnmarshalException("foo");
}
return realAdapter.unmarshal(input);
}

});

Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_1), is(true));
}

问题

虽然 thenAnswer 方法有效,但它似乎不是正确的解决方案。针对这种情况执行部分模拟的正确方法是什么?

最佳答案

我不是完全确定你的 mock 和 spy 是什么意思,但你真的只需要在这里 mock 。

首先,无论出于何种原因,我在尝试您的模拟时遇到了一些障碍。我相信这与 spy 调用有关,它在某种程度上被搞砸了。我最终确实克服了这些,但我想通过一些简单的事情。

接下来,我确实注意到你的 spy 方式有些不对劲(我的方法的基础):

MyHelper spied = spy(fixture.adapter);

这意味着您希望模拟 MyHelper 的实例, 进行监视。最糟糕的是,即使这个对象完全水合,它也不会被正确注入(inject),因为你没有将它重新分配给测试对象(我假设是 fixture)。

我的偏好是使用 MockitoJUnitRunner 来帮助注入(inject)模拟实例,从那里我建立了我实际需要模拟的基础。

只有一个模拟实例,然后是测试对象,这个声明将确保它们都被实例化和注入(inject):

@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {

@Mock
private MyHelper adapter;

@InjectMocks
private MyService fixture;
}

我们的想法是,您正在将模拟注入(inject) 夹具。您不必使用它 - 您可以在 @Before 声明中使用标准 setter ,但我更喜欢这个,因为它大大减少了您必须编写的样板代码才能使模拟工作。

现在只需要做一个更改:删除 spy 实例并将其之前的用法替换为实际的模拟。

doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));

随着所有代码的提升,这通过了:

@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock
private MyHelper adapter;

@InjectMocks
private MyService fixture;

@Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {

doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));

Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(Example.EntryType.TYPE_1), is(true));
}
}

不想让问题/用例不完整,我绕了一圈,用内部类替换了测试,它也工作得很好:

@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock
private Example.MyHelper adapter;

@InjectMocks
private Example.MyService fixture;

@Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {

doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));

Example.Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(Example.EntryType.TYPE_1), is(true));
}
}

关于java - 如何使用 Mockito 部分模拟抛出异常的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29994576/

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