是否可以使用 EasyMock 创建一个实现多个接口的模拟对象?
Posted
技术标签:
【中文标题】是否可以使用 EasyMock 创建一个实现多个接口的模拟对象?【英文标题】:Is it possible to create a mock object that implements multiple interfaces with EasyMock? 【发布时间】:2010-11-13 07:56:59 【问题描述】:例如接口Foo
和接口Closeable
?
在 Rhino Mocks 中,您可以在创建 mock 对象时提供多个接口,但 EasyMock 的 createMock()
方法只接受一种类型。
是否可以使用 EasyMock 来实现这一点,而无需求助于创建一个扩展 Foo
和 Closeable
的临时接口,然后对其进行模拟?
【问题讨论】:
【参考方案1】:虽然我基本上同意尼克霍尔特的回答,但我想我应该指出mockito 允许通过以下调用执行您所要求的操作:
Foo mock = Mockito.mock(Foo.class, withSettings().extraInterfaces(Bar.class));
显然,当您需要将模拟用作Bar
但该演员不会抛出ClassCastException
时,您必须使用演员表:(Bar)mock
这是一个更完整的例子,尽管完全荒谬:
import static org.junit.Assert.fail;
import org.junit.Test;
import static org.mockito.Mockito.*;
import org.mockito.Mockito;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import org.hamcrest.Matchers;
import java.util.Iterator;
public class NonsensicalTest
@Test
public void testRunnableIterator()
// This test passes.
final Runnable runnable =
mock(Runnable.class, withSettings().extraInterfaces(Iterator.class));
final Iterator iterator = (Iterator) runnable;
when(iterator.next()).thenReturn("a", 2);
doThrow(new IllegalStateException()).when(runnable).run();
assertThat(iterator.next(), is(Matchers.<Object>equalTo("a")));
try
runnable.run();
fail();
catch (IllegalStateException e)
【讨论】:
【参考方案2】:你有没有考虑过类似的事情:
interface Bar extends Foo, Closeable
然后模拟界面Bar?
【讨论】:
是的,我只是想看看是否可以避免这种情况。根据我的问题:“是否可以使用 EasyMock 来实现这一点,而无需求助于创建一个扩展 Foo 和 Closeable 的临时接口,然后对其进行模拟?” 我喜欢这个解决方案 - 在测试类中创建这样的接口对我来说没问题,好主意 ;-)【参考方案3】:EasyMock 不支持此功能,因此您只能使用临时界面的回退。
顺便说一句,我闻到了一点代码的味道——一个方法真的应该把一个对象当作两个不同的东西,在这种情况下是Foo
和Closeable
接口吗?
这对我来说意味着该方法正在执行多个操作,虽然我怀疑其中一个操作是“关闭”Closeable
,但调用代码决定是否关闭'是必需的?
以这种方式构造代码将“打开”和“关闭”保持在同一个 try ... finally
块中,恕我直言,让代码更具可读性,更不用说该方法更通用,并允许您传递仅实现 Foo
的对象.
【讨论】:
我同意这一点,但扩展一下:如果您使用依赖注入,并且您的类同时需要 Foo 和 Closable,那么您真的应该有两个单独的设置器。如果您选择为这两个注入相同的对象,那很好,但我认为被测类不需要知道它们是同一个对象 - 它应该将 Foo 视为 Foo 并将 Closeable 视为一个可关闭的 尼克,马特,感谢您的意见。只是为了澄***景,上下文是 Foo 是模块化插件系统的接口。第三方模块实现 Foo,然后被框架实例化和使用。它们还可以选择实现 Closeable,在这种情况下,框架将在使用完它们后关闭它们。因此,单元测试需要涵盖两种不同的场景:同样可关闭的 Foo 和不可关闭的 Foo。我希望这是有道理的。 @NickHolt:我不会完全同意你的看法。考虑当你有一个接口Person
,它只有getter(getFirstName()
,getAddress()
,...)和接口ModifiablePerson
,它只有setter(setFirstName()
,setAddress()
,...) )。现在您想测试一个 SUT,它采用 Person
,但检查是否通过了对象 instanceof ModifiablePerson
并基于此执行一些操作。 Closeable
也是一个很好的例子:如果对象提供“扩展”功能,instanceof
明确检查并加以利用,那有什么不好的?【参考方案4】:
most voted answer 的替代方案仍然基于 Mockito,但带有注释。您可以直接从Mock
注释中将extraInterfaces
设置为下一个:
@RunWith(MockitoJUnitRunner.class)
public class MyTest
@Mock(extraInterfaces = Closeable.class)
private Foo foo;
...
注意: extraInterfaces
的类型为 Class<?>[]
,因此您可以根据需要指定多个接口。
如果您需要模拟额外接口的方法调用,则需要强制转换您的模拟。例如,假设我想在我的模拟foo
上调用close()
时抛出一个IOException
,相应的代码将是:
Mockito.doThrow(IOException.class).when((Closeable) foo).close();
【讨论】:
我试过了,但仍然出现投射异常,这是我所缺少的。我正在使用 @RunWith(PowerMockRunner.class) 运行我的测试用例 @MANJITKUMAR 我刚刚用 powermockito 2.0.2 进行了测试,它对我有用 我使用的是 1.5.5 powermock 版本【参考方案5】:据我所知,唯一明确支持模拟多个接口的 Java 模拟工具是 JMockit。 (我添加此功能的灵感来自 Moq 和 Rhino Mocks,它们都是 .NET 工具。)
一个例子(来自mockit.ExpectationsUsingMockedTest
JUnit 4 测试类):
@Test
public <M extends Dependency & Runnable> void mockParameterWithTwoInterfaces(final M mock)
new Expectations()
mock.doSomething(true); returns("");
mock.run();
;
assertEquals("", mock.doSomething(true));
mock.run();
Dependency
和 Runnable
是接口。 doSomething
方法属于第一种,run
属于第二种。
【讨论】:
这个 sn-p 似乎没有创建模拟对象。你会怎么做?【参考方案6】:解决这个问题的另一种方法是使用CGLib mixin:
final Interface1 interface1 = mockery.mock(Interface1.class);
final Interface2 interface2 = mockery.mock(Interface2.class);
service.setDependence(Mixin.create(new Object[] interface1, interface2 ));
mockery.checking(new Expectations()
oneOf(interface1).doSomething();
oneOf(interface2).doNothing();
);
service.execute();
这是否是个好主意,有待讨论......
【讨论】:
以上是关于是否可以使用 EasyMock 创建一个实现多个接口的模拟对象?的主要内容,如果未能解决你的问题,请参考以下文章