是否可以使用 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 来实现这一点,而无需求助于创建一个扩展 FooCloseable 的临时接口,然后对其进行模拟?

【问题讨论】:

【参考方案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 不支持此功能,因此您只能使用临时界面的回退。

顺便说一句,我闻到了一点代码的味道——一个方法真的应该把一个对象当作两个不同的东西,在这种情况下是FooCloseable 接口吗?

这对我来说意味着该方法正在执行多个操作,虽然我怀疑其中一个操作是“关闭”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&lt;?&gt;[],因此您可以根据需要指定多个接口。

如果您需要模拟额外接口的方法调用,则需要强制转换您的模拟。例如,假设我想在我的模拟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();

DependencyRunnable 是接口。 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 创建一个实现多个接口的模拟对象?的主要内容,如果未能解决你的问题,请参考以下文章

模拟对象测试——EasyMock

在 pyqtgraph 中的多个图上链接自动缩放

EasyMock:无效方法

java 检查对象是否处于重播状态(EasyMock)

Easy Mock的使用

EasyMock:如何在没有警告的情况下创建泛型类的模拟?