Могу ли я издеваться над вызовом метода суперкласса?

Иногда вы хотите протестировать метод класса и сделать ожидание вызова метода суперкласса. Я не нашел способа сделать это ожидание в java с помощью easymock или jmock (и я думаю, что это невозможно).

Существует (относительно) чистое решение для создания делегата с логикой метода суперкласса, а затем установки ожиданий от него, но я не знаю, почему и когда использовать это решение — есть идеи/примеры?

Спасибо


person arielsan    schedule 07.03.2009    source источник
comment
Я не могу придумать действительно вескую причину, почему это плохая идея. +1 за блестящий вопрос.   -  person SingleNegationElimination    schedule 08.03.2009


Ответы (6)


Ну, ты можешь, если хочешь. Не знаю, знакомы ли вы с JMockit, попробуйте. Текущая версия 0.999.17 А пока давайте посмотрим на нее...

Предположим следующую иерархию классов:

public class Bar {
    public void bar() {
        System.out.println("Bar#bar()");
    }
}

public class Foo extends Bar {
    public void bar() {
        super.bar();
        System.out.println("Foo#bar()");
    }
}

Затем, используя JMockit в своем FooTest.java, вы можете подтвердить, что вы действительно вызываете Bar из Foo.

@MockClass(realClass = Bar.class)
public static class MockBar {
    private boolean barCalled = false;

    @Mock
    public void bar() {
        this.barCalled = true;
        System.out.println("mocked bar");
    }
}

@Test
public void barShouldCallSuperBar() {
    MockBar mockBar = new MockBar();
    Mockit.setUpMock(Bar.class, mockBar);

    Foo foo = new Foo();
    foo.bar();

    Assert.assertTrue(mockBar.barCalled);

    Mockit.tearDownMocks();
}
person Cem Catikkas    schedule 08.03.2009
comment
Это также можно сделать с помощью JMockit Expectations API (см. новый сайт проекта по адресу code.google.com/). p/jmockit). - person Rogério; 12.01.2010

Расширение ответа @Cem Catikkas с использованием JMockit 1.22:

@Test
public void barShouldCallSuperBar() {
    new MockUp<Bar>() {
        @Mock
        public void bar() {
            barCalled = true;
            System.out.println("mocked bar");
        }
    };

    Foo foo = new Foo();
    foo.bar();

    Assert.assertTrue(mockBar.barCalled);
}

Нет необходимости в статическом классе, аннотированном @MockClass, он заменяется классом MockUp.

person Oliver Hernandez    schedule 28.03.2016

Я не думаю, что стал бы издеваться над супервызовом - мне кажется, что поведение там является частью поведения самого класса, а не поведения зависимости. Насмешка всегда кажется, что она должна быть связана с зависимостями больше, чем с чем-либо еще.

У вас есть хороший пример вызова, который вы хотите сымитировать? Если вы хотите имитировать такой вызов, стоит ли рассматривать композицию вместо наследования?

person Jon Skeet    schedule 08.03.2009
comment
Да, но вы хотите протестировать изолированный подкласс, вы знаете, что суперкласс работает нормально, и вы не хотите тестировать его снова. - person arielsan; 08.03.2009
comment
Пожалуйста, просто не делай этого. Вы нарушаете инкапсуляцию, чтобы заблокировать реализацию в тесте. Если этот код никогда больше не изменится, вы пожалеете об этом. - person Steve Freeman; 13.03.2011

Есть несколько тестов, которые делают именно это (т. е. определяют ожидаемый вызов метода суперкласса) с использованием JMockit Expectations API в наборе примеров тестов Animated Transitions. Например, FadeInTest.

person Rogério    schedule 21.06.2009
comment
Тем временем этот тест был перемещен по адресу: code.google.com/p/jmockit/source/browse/trunk/samples/ - person Konstantin Pribluda; 08.11.2011
comment
Снова перемещено: github.com/jmockit/jmockit1/blob/master/samples/ - person jordanpg; 25.09.2015

Нет, с помощью jMock нельзя имитировать методы суперкласса.

Однако есть быстрое и грязное решение вашей проблемы. Предположим, у вас есть класс A и класс B, расширяющий A. Вы хотите имитировать метод A.a() для B. Вы можете ввести класс C, расширяющий B, в свой тестовый код и переопределить метод C.a() (просто вызовите super или верните null, id не важно). После этого имитируйте C и используйте макет везде, где вы использовали бы B.

person Vazha Amiranashvili    schedule 05.03.2012
comment
Это не так грязно. Мы можем создать класс C как внутренний в тестовом классе. - person Gangnus; 27.01.2016
comment
На самом деле это не работает, если вы вызываете суперметод с тем же именем. B.a() вызывает A.a(). Если вы вводите C.a() и вызываете super, вы просто получаете C.a() -> B.a() -> A.a() - person david.tanner; 14.03.2018

перехват супервызова слишком мелкий. Не переусердствуйте с изоляцией.

person Steve Freeman    schedule 21.05.2009