Возможно ли создать макет объекта, который реализует несколько интерфейсов с помощью EasyMock?

Возможно ли создать макетный объект, который реализует несколько интерфейсов с помощью EasyMock?

Например, интерфейс Foo и интерфейс Closeable?

В Rhino Mocks вы можете предоставить несколько интерфейсов при создании макетного объекта, но метод EasyMock createMock() принимает только один тип.

Возможно ли достичь этого с помощью EasyMock, не прибегая к резервному созданию временного интерфейса, который расширяет как Foo, так и Closeable, а затем насмехается над этим?

Ответ 1

EasyMock не поддерживает это, поэтому вы задерживаетесь с возвратом временного интерфейса.

В стороне, я чувствую немного кода wiff - должен ли метод действительно обрабатывать объект как 2 разные вещи, интерфейс Foo и Closeable в этом случае?

Это означает, что метод выполняет несколько операций, и, хотя я подозреваю, что одна из этих операций заключается в "закрытии" Closeable, не имеет ли смысл, чтобы вызывающий код решал, закрыть '?

Структурирование кода таким образом сохраняет "открытые" и "закрытые" в одном и том же блоке try ... finally, а IMHO делает код более удобочитаемым, не говоря уже о более общем методе и позволяет передавать объекты, которые реализуют только Foo.

Ответ 2

Хотя я принципиально согласен с ответом Ника Холта, я думал, что должен указать, что mockito позволяет делать то, что вы просите, со следующим call:

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

Очевидно, вам придется использовать cast: (Bar)mock, когда вам нужно использовать макет как Bar, но этот прилив не будет бросать 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) {
        }
    }

Ответ 3

Считаете ли вы что-то вроде:

interface Bar extends Foo, Closeable {
}

а затем mock interface Bar?

Ответ 4

Альтернатива самого голосованного ответа по- прежнему основана на Mockito, но с аннотациями. Вы можете установить extraInterfaces непосредственно из аннотации Mock следующим образом:

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

NB: extraInterfaces имеет тип Class<?>[] Поэтому при необходимости вы можете указать несколько интерфейсов.

Если вам нужно высмеять вызовы методов дополнительных интерфейсов, вам нужно будет сделать свой макет. Например, позвольте сказать, что я хочу бросить IOException когда я вызываю close() на мой mock foo, соответствующий код будет тогда:

Mockito.doThrow(IOException.class).when((Closeable) foo).close();

Ответ 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 - второму.

Ответ 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();

Является ли это хорошей идеей, это что-то до обсуждения...