50

是否可以使用 EasyMock 创建一个实现多个接口的模拟对象?

例如,接口Foo和接口Closeable

在 Rhino Mocks 中,您可以在创建 mock 对象时提供多个接口,但 EasyMock 的createMock()方法只接受一种类型。

是否可以使用 EasyMock 来实现这一点,而无需求助于创建一个同时扩展Fooand的临时接口,Closeable然后对其进行模拟?

4

6 回答 6

83

尽管我从根本上同意尼克霍尔特的回答,但我想我应该指出,mockito允许通过以下调用执行您所要求的操作:

Foo mock = Mockito.mock(Foo.class, withSettings().extraInterfaces(Bar.class));

显然,您必须使用演员表:(Bar)mock当您需要将模拟用作 aBar但演员表不会抛出时ClassCastException

这是一个更完整的示例,尽管完全荒谬:

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) {
        }
    }
于 2009-08-13T08:14:18.117 回答
14

你有没有考虑过类似的事情:

interface Bar extends Foo, Closeable {
}

然后模拟界面吧?

于 2009-07-23T09:58:30.817 回答
10

EasyMock 不支持此功能,因此您只能使用临时界面的回退。

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

这对我来说意味着该方法正在执行多个操作,虽然我怀疑其中一个操作是“关闭” Closeable,但调用代码决定是否需要“关闭”不是更有意义吗?

以这种方式构造代码将“打开”和“关闭”保持在同try ... finally一块中,恕我直言,使代码更具可读性,更不用说该方法更通用,并允许您传递仅实现的对象Foo

于 2009-07-23T10:41:51.197 回答
7

投票最多的答案的替代方案仍然基于Mockito但带有注释。您可以extraInterfaces直接从Mock注释中设置如下:

@RunWith(MockitoJUnitRunner.class)
public class MyTest {
    @Mock(extraInterfaces = Closeable.class)
    private Foo foo;
    ...
}

注意: extraInterfaces是类型Class<?>[],因此您可以根据需要指定多个接口。

如果您需要模拟额外接口的方法调用,则需要强制转换您的模拟。例如,假设我想IOException在调用close()mock时抛出一个foo,相应的代码将是:

Mockito.doThrow(IOException.class).when((Closeable) foo).close();
于 2018-04-18T18:23:39.637 回答
2

据我所知,唯一明确支持模拟多个接口的 Java 模拟工具是JMockit。(我添加此功能的灵感来自 Moq 和 Rhino Mocks,它们都是 .NET 工具。)

一个示例(来自mockit.ExpectationsUsingMockedTestJUnit 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

于 2009-07-26T17:57:08.050 回答
2

解决此问题的另一种方法是使用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();

这是否是一个好主意,这有待讨论......

于 2014-01-10T15:50:25.100 回答