Как мне высмеивать объекты, которые я не могу создать в своих тестах? - PullRequest
9 голосов
/ 26 января 2011

Я использую EasyMock для проверки объектов в моих тестах.Но как мне высмеивать объекты, созданные где-то еще в моем коде?Посмотрите на следующий псевдокод.Я хочу издеваться над WebService # getPersonById, как мне это сделать?

public class Person {
  public Person find(int id) {
    WebService ws = new WebService();
    return ws.getPersonById(id);
  }
}

public class PersonTest {
  testFind() {
    // How do I mock WebService#getPersonById here?
  }
}

Ответы [ 6 ]

9 голосов
/ 26 января 2011

Mocking обычно работает хорошо, если вы используете инверсию управления и внедрение зависимостей для подключения ваших услуг.Таким образом, ваш человек должен выглядеть так:

public class Person() {
  WebService ws = null;

  // or use setters instead of constructor injection
  Persion(WebService ws) {
     this.ws = ws;
  }
  public Person find(int id) {
    return ws.getPersonById(id);
  }
}

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

В вашей реальной среде контейнер IoC внедрит реальный веб-сервис в.

Теперь, если вы не хотите иметь дело со всем этим IoC, вам нужно отсоединить ваш веб-сервис от вашего Person (который должен быть вызван PersonService или чем-то, а не просто Person, что обозначает сущность).Другими словами, при написании кода вы можете использовать только один тип WebService.Вам нужно сделать так, чтобы Person просто нуждался в некотором типе WebService, а не в конкретном, который вы жестко запрограммировали.

Наконец, в написанном коде WebService является классом, а не интерфейс.WebService должен быть интерфейсом, и вы должны реализовать его.EasyMock хорошо работает с интерфейсами;он может быть в состоянии смоделировать конкретные классы (это было давно, так как я фактически использовал его), но в качестве принципа проектирования вы должны указать требуемый интерфейс, а не конкретный класс.

3 голосов
/ 14 февраля 2011

Нет способа сделать это с EasyMock (или с большинством других API-интерфейсов).С другой стороны, с JMockit такой тест был бы очень простым и элегантным:

public class PersonTest
{
    @Test
    public testFind(@Mocked final WebService ws) {
        final int id = 123;

        new NonStrictExpectations() {{
            ws.getPersonById(id); result = new Person(id);
        }};

        Person personFound = new Person().find(id);

        assertEquals(id, personFound.getId());
    }
}

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

1 голос
/ 28 января 2011

Я думаю, что вы упускаете гораздо большую проблему.Сложность в тестировании состоит в том, чтобы попытаться что-то сказать вам, что наличие объекта Person (часть домена), который также использует удаленный сервис для поиска других экземпляров себя (часть системы), смешивает проблемы.Отделите получение Person s от объекта Person, и вы получите более чистый и переносимый код.

Не путайте непосредственное удобство (у меня в руке есть объект Person, поэтому я буду использовать его для получения большего) с удобством сопровождения.

0 голосов
/ 07 мая 2016

Вы можете попробовать использовать PowerMock, когда метод New

https://github.com/jayway/powermock/wiki/MockitoUsage#how-to-mock-construction-of-new-objects

Больше примеров здесь - http://www.programcreek.com/java-api-examples/index.php?api=org.powermock.api.mockito.PowerMockito

Ваш код может выглядеть как -

whenNew(WebService.class).withAnyArguments().thenReturn(yourMockInstanceToWebServiceClass);

0 голосов
/ 27 января 2011

В итоге я использовал JMockit .Он поддерживает насмешку над всеми экземплярами класса.

0 голосов
/ 26 января 2011

Прежде всего вам нужно сделать ws макетом, обычно путем инъекции.

public abstract class Person() {
  public Person find(int id) {
    WebService ws = createWebService();
    return ws.getPersonById(id);
  }
  protected abstract WebService createWebService();
}

Затем вы можете вставить его и использовать EasyMock.expect для настройки возврата

public class PersonTest() {
  testFind() {
    WebService mock = EasyMock.createMock(WebService.class);
    Person p = new Persion() {
      protected WebService createWebService() {
        return mock;
      }
    }
    EasyMock.expect(mock.getPersonById()).andReturn(dummyValue);
    //Test code
  }
}

Вам также понадобится PersonImpl, чтобы иметь реальный метод создания.

...