Интерфейс Junit Powermock

Отредактировано: переписал вопрос короче. Спасибо за указание на это! :)

Мне нужно написать тесты JUnit, где я должен издеваться над парой частных методов и полей без установки/получения. Я попробовал два способа сделать это: с Mockito и PowerMock. Вопрос в том, можно ли их совместить? Первая попытка протестировать метод с закрытым полем:

...
@Mock private XMLConfiguration config;
@InjectMocks AAIGroupController groupi;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}
...

    given(config.configurationsAt(anyString())).willReturn(ldapGroups);
...

Вторая попытка PowerMock протестировать закрытый метод:

...
AAIGroupController groupC = PowerMockito.spy(new AAIGroupController());
...
when(groupC, method(AAIGroupController.class, "getLdapGroupNodeFromGroupConfigFile", ELUser.class))
        .withArguments(any(ELUser.class))
        .thenReturn(sn);
assertTrue(iaigroupi.isPosixAccount(new ELUserAAI(null, true, false)));
...

Есть ли способ их объединить, или я должен использовать только PowerMock? У меня возникли проблемы с насмешкой над частным полем с помощью PowerMock.

Другой вопрос: мне нужно написать тесты JUnit для интерфейсов. Разве это ерунда, так как классы, реализующие интерфейсы, уже протестированы. И если нет, то что было бы хорошей практикой для этого?

Заранее спасибо!

Старый вопрос, можно вообще пропустить. Я просто оставил это здесь для понимания уже данных ответов.

Мне нужно написать тесты JUnit для интерфейсов, где реализация уже сделана. Но я немного запутался, как правильно писать для них тесты. Я начал с реализации, но сейчас застрял. Во-первых, я не уверен, правильно ли я тестирую интерфейсы, а затем возникает проблема с насмешкой над частным полем. До сих пор я написал два разных тестовых класса, чтобы попробовать что-то. Оба тестовых класса «работают» прямо сейчас, но я хотел бы объединить их в класс InterfaceAAIGroupControllerTest, но не знаю, как использовать Mockito в PowerMock. Для интерфейса я использую

groupi = PowerMockito.spy(new AAIGroupController());

и для другого теста я использую

@Mock private XMLConfiguration config;
@InjectMocks AAIGroupController groupi;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

Для кода:

Интерфейс:

public interface IAAIGroupController {

/**
* Appends a new group entry
*/
public void appendGroup();

/**
 * checks if a given account is a possix account
 * 
 * @param elUser the user object to check
 * @return true if is posix, otherwise false
 * @throws ConfigurationException
 */
public boolean isPosixAccount(ELUser elUser)

...

Реализация

@Override
public void appendGroup() {
    try {
        config.addProperty("ldapGroup(-1)",

        List<SubnodeConfiguration> ldapGroups = config.configurationsAt("ldapGroup");
        SubnodeConfiguration sn = ldapGroups.get(ldapGroups.size()-1);
        try {
            sn.addProperty("script(-1)", null);
            // TODO Configuration 
        } catch (Exception e) {
            sn.addProperty("script(-1)", "default.sh");
        }

        config.save();
    } catch (ConfigurationException ex) {
        LOGGER.log(Level.SEVERE, ex.getMessage());
    }
}

@Override
public boolean isPosixAccount(final ELUser elUser) throws ConfigurationException {
    SubnodeConfiguration ldapGroup = getLdapGroupNodeFromGroupConfigFile(elUser);
    String posix = ldapGroup.getString("[@posix]");
    if (posix == null || posix.isEmpty() || posix.equalsIgnoreCase("true")) {
        return true;
    } else if (posix.equalsIgnoreCase("false")) {
        return false;
    } else {
        throw new ConfigurationException("posix attribute is not set properly!");
    }
}

Код тестирования интерфейса, который пока плохо обрабатывает исключения, исправит это, когда заработает. Я использую PowerMock, так как мне нужно вызывать частные методы в реализации. Я не знаю, смогу ли я хорошо тестировать интерфейсы таким образом.

@RunWith(PowerMockRunner.class)
@PrepareForTest(AAIGroupController.class)
public class InterfaceAAIGroupControllerTest {

    public IAAIGroupController iaigroupi;
    public AAIGroupController groupi;
    public XMLConfiguration config;


    @Before
    public void setUp() {
        groupi = PowerMockito.spy(new AAIGroupController());
        config = Whitebox.getInternalState(groupi, "config");
        iaigroupi = groupi;
    }

    @Test
    public void isPosixAccount_True() {
        try {
        SubnodeConfiguration sn = mock(SubnodeConfiguration.class);
        when(iaigroupi, method(AAIGroupController.class, "getLdapGroupNodeFromGroupConfigFile", ELUser.class))
        .withArguments(any(ELUser.class))
        .thenReturn(sn);

            assertTrue(iaigroupi.isPosixAccount(new ELUserAAI(null, true, false)));
        } catch (ConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

Тестовый код для реализации, здесь я использую Mockito для имитации частного поля, которое используется в коде, но никогда не устанавливается конструктором или методом.

@RunWith(MockitoJUnitRunner.class)
public class AAIGroupControllerTest {

    @Mock private XMLConfiguration config;
    @InjectMocks AAIGroupController groupi;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void appendGroupTest() {
        List<SubnodeConfiguration> ldapGroups = mock(List.class);
        SubnodeConfiguration sn = mock(SubnodeConfiguration.class);
        given(config.configurationsAt(anyString())).willReturn(ldapGroups);
        given(ldapGroups.size()).willReturn(11);
        given(ldapGroups.get(ldapGroups.size()-1)).willReturn(sn);
        groupi.appendGroup();
        verify(sn).addProperty("script(-1)", null);
    }

}

Мои проблемы прямо сейчас: могу ли я протестировать интерфейс, как я? Не кажется хорошим решением. Параметризация кажется ненужной, так как всегда будет только одна реализация, и я понятия не имею, как использовать два бегуна. Другое дело, как я могу использовать тестовый код из последнего примера, где я издеваюсь над частным полем «config», в тестовом классе PowerMock? Я не знаю, как правильно издеваться над частным полем и изменять его поведение, когда из него вызывается метод.

Заранее спасибо за каждый совет!


person Redmo    schedule 07.05.2013    source источник


Ответы (1)


При чтении этого на ум приходят три мысли:

  • вам действительно нужно было опубликовать так много кода, и вы действительно ожидали, что мы прочитаем его весь? Ваш вопрос очень расплывчатый и трудный для понимания. Подумайте о том, чтобы задавать по одному вопросу за раз.
  • вы не тестируете интерфейсы. В интерфейсе нет кода, что тут тестировать?
  • если вы используете PowerMock для насмешек, не используйте также Mockito в том же тесте. Используйте тот или иной в соответствии с вашими требованиями

Чтобы установить частное поле, либо предоставьте установщик (возможно в области по умолчанию), либо используйте отражение (например, ReflectionTestUtils) для его установки.

person John B    schedule 07.05.2013
comment
Вы просто должны учитывать, что плакаты здесь не оплачиваются, и в основном это делается просто для удовольствия. Если нам нужно прополоть тонну кода и если вопрос не будет кратким, большинство людей просто перейдут к другому вопросу. Вы получите наибольшее количество ответов, если ваш вопрос легко читается. - person John B; 07.05.2013
comment
Спасибо за ответы. Я здесь впервые, не знал, сколько публиковать, поэтому я подумал, что лучше слишком много информации, чем недостаточно, ну... :S Это требование для тестирования интерфейса, и я думаю, что общедоступный API, выраженный через его интерфейс , является важным моментом для проверки. Приятно знать, поэтому я разделю их по тесту. В следующий раз, если он будет, я постараюсь сжать свой вопрос и опубликовать только важные моменты. Еще раз спасибо за полезные советы. :) - person Redmo; 07.05.2013
comment
Тот, кто придумал это требование, нуждается в уроке разработки программного обеспечения или путается между интерфейсом и абстрактным классом. Если вы пытаетесь протестировать интерфейс, все, что вы тестируете, — это фиктивный фреймворк. Одним из допустимых вариантов является разработка теста для API/интерфейса, а затем передача ему одной или нескольких конкретных реализаций для проверки, чтобы убедиться, что они правильно реализуют интерфейс. Этот дизайн позволяет повторно использовать тесты, если существует несколько реализаций одного и того же интерфейса. - person John B; 07.05.2013
comment
Тогда я буду проводить тестирование JUnit только для реализаций. Что довольно логично. Тестирование API происходит во время интеграционного теста, так что это следует охватить. Спасибо, что нашли время ответить новичку. :) - person Redmo; 07.05.2013