Java: Как мне высмеять метод поля, когда это поле не отображается?

Я использую Java 6, JUnit 4.8.1 и пишу консольное приложение. В моем приложении есть поле участника, которое не отображается...

public class MyApp { 
    ...
    private OpportunitiesService m_oppsSvc;

    private void initServices() { 
        …
        m_oppsSvc = new OpportunitiesServiceImpl(…);
    }
    ...
}

Я хочу издеваться над таким поведением, что всякий раз, когда вызывается один метод из моей службы (например, m_oppsSvc.getResults()), тот же результат всегда возвращается. Как мне это сделать? Нет никакого метода установки для поля. В настоящее время я работаю с Mockito 1.8.4. Можно ли сделать это с помощью Mockito или какой-либо другой фальшивой структуры?

Ответы

Ответ 1

Это то, что вы хотите:

@RunWith(MockitoJUnitRunner.class)
public class MyAppTest { 

    @Mock private OpportunitiesService mocked_m_oppsSvc;
    @InjectMocks MyApp myApp;

    @Test public void when_MyApp_uses_OpportunititesService_then_verify_something() { 
        // given
        given( mocked_m_oppsSvc.whatever()).willReturn(...);

        // when
        myApp.isUsingTheOpportunitiesService(...);

        // then
        verify...
        assertThat...
    }
}

Использование: Mockito 1.9.0, Стиль BDD, FEST-Assert AssertJ.

Надеюсь, что помогает:)

Ответ 2

Вам следует подумать о попытках фальсифицировать личное поле smell. То есть признак того, что либо то, что вы пытаетесь сделать, либо неверно, либо что ваш код в настоящее время структурирован неправильно. Вам нужно только обмануть общедоступные методы или вложенные зависимости

В коде, который вы указали, вам следует рассмотреть возможность внедрения функции OpportunitiesService следующим образом:

public class MyApp { 
    ...
    private OpportunitiesService m_oppsSvc;

    public MyApp(OpportunitiesService oppsSvc) {
        this.m_oppsSvc = oppsSvc;
    }
    ...
}

В своем тесте вы можете ввести макет следующим образом:

OpportunitiesService mockOpportunitiesService =
    Mockito.mock(OpportunitiesService.class);
Mockit.when(mockOpportunitiesService.someMethod()).thenReturn(someValue);
MyApp app = new MyApp(mockOpportunitiesService);

Ответ 3

Учитывая, что вы уже используете mockito, почему бы просто не использовать отражение:

@RunWith(MockitoJUnitRunner.class)
public class MyApp { 

    @Mock
    private OpportunitiesService m_oppsSvc;

    private MyApp myApp;


    @Before
    public void before() throws Exception {
       myApp = new MyApp();
       Field f = MyApp.class.getDeclaredField("m_oppsSvc");
       f.setAccessible(true);
       f.set(myApp, m_oppsSvc);
    }
}

Это немного уродливо, но это будет трюк. Обратите внимание, что это может быть не самый эффективный способ сделать это с Mockito, но он будет работать.

Там также Powermock, который должен позволить вам сделать это, также используя класс Whitebox. Я не буду разбираться во всех деталях Powermock, но здесь призыв ввести значение частного поля, которое должно быть макетным объектом:

Whitebox.setInternalState(myApp, "m_oppsSvc", m_oppsSvc);

Ответ 4

Вы можете легко сделать это с помощью JMockit:

public class MyAppTest
{
    @Tested MyApp myApp;

    @Test
    public testSomething(final @Capturing OpportunitiesService mockService)
    {
        new NonStrictExpectations() {{
            mockService.getResults(); result = asList("a", "b", "C");
            // record other expectations, if needed
        }};

        myApp.whateverMethodIWantToTest();

        new Verifications() {{
            mockService.doSomething(anyInt);
            // verify other expectations, if needed
        }};
    }
}

Даже если класс реализации OpportunitiesServiceImpl не упоминается в тестовом коде, его экземпляры (любое их число) будут по-прежнему правильно издеваться.

Ответ 5

Как правило, вы должны использовать инъекцию зависимостей и передавать макет (тип OppportunitiesServiceImpl) через конструктор, отдельный сеттер или непосредственно к методу (getResults). Возможно, вам сначала понадобится извлечь интерфейс для OpportunitiesServiceImpl.

Ответ 6

Обычно это решается с помощью инъекции зависимостей. В обычном (производственном) режиме ваш контейнер инъекции зависимостей (например, Spring или Guice) будет вставлять экземпляр OpportunitiesService в MyApp через ваш конструктор или через сеттер.

Затем, когда вы тестируете, вы можете "ввести" экземпляр mock вручную, используя тот же аргумент setter или constructor.

Вместо того, чтобы делать

m_oppsSvc = new OpportunitiesServiceImpl(…);

Попробуйте выполнить OpportunitesService через конструктор MyApp