Нужен совет по модульному тестированию с использованием фиктивного объекта - PullRequest
2 голосов
/ 11 июня 2010

Я только недавно прочитал о «Mocking objects» для модульного тестирования, и в настоящее время у меня возникают трудности с реализацией этого подхода в моем приложении.Пожалуйста, позвольте мне объяснить мою проблему.

У меня есть класс модели User, который зависит от 2 источников данных (базы данных и веб-службы facebook).Класс контроллера просто использует эту модель User в качестве интерфейса для доступа к данным, и ему все равно, откуда эти данные.

В настоящее время я никогда не проводил никаких модульных тестов этой модели User, потому что оназависит от внешнего веб-сервиса.Но некоторое время назад я читал об имитации объектов, и теперь я знаю, что это общий подход к модульному тестированию класса, который зависит от внешних ресурсов (как в моем случае).

Теперь я хочу создатьмодульное тестирование для модели User, но затем я столкнулся с проблемой проектирования: чтобы модель User использовала поддельный SDK Facebook, я должен внедрить этот поддельный SDK Facebook в объект User (возможно, с помощью установщика),Поэтому я не могу создать SDK Facebook внутри объекта User.Мне нужно построить его вне объекта User, а внедрить SDK в объект User.

Настоящим клиентом моей модели User является контроллер приложения.,Поэтому я должен создать SDK Facebook внутри контроллера и внедрить его в объект пользователя.Ну, это проблема, потому что я хочу, чтобы мой контроллер был максимально чистым.Я хочу, чтобы мой контроллер не знал об источнике данных приложения.

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

С уважением, Андре

PS: Я использую Zend Framework, PHP 5.3.

Ответы [ 5 ]

2 голосов
/ 12 июня 2010

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

Поскольку вы не указали свой язык, я покажу вам пример на Java, надеюсь, это поможет понять:

class User {
  private DataBase database;
  private WebService webService;

  // default constructor
  public User() {
    database = new OracleDataBase();
    webService = new FacebookWebService();
  }

  // constructor for unit testing
  public User(DataBase database, WebService webService) {
    this.database = database;
    this.webService = webService;
  }
}
1 голос
/ 12 июня 2010

Дело не в насмешках, а в зависимостях - и попытка юнит-тестирования вызвала проблему. Похоже, что в настоящее время вы создаете свой пользовательский объект в вашем контроллере.

Если пользователь и контроллер имеют одинаковое время жизни (они созданы одновременно), то вы можете передать пользователя в конструктор контроллера, в котором можно выполнить замену.

Если в вызове присутствует объект User, то, возможно, объект User должен быть передан средой или возвращен из некоторого объекта Context.

0 голосов
/ 11 июня 2010

Вы не говорите, какой язык вы используете, но я использую Ruby и Mocha для насмешки объектов, и это довольно просто.

См. http://mocha.rubyforge.org/.

здесь четвертый пример показываетчто любой вызов Product.name из тестируемого модуля перехватывается, и возвращается значение 'stubbed_name'.

Я полагаю, что в Java есть похожие механизмы и т. д.

0 голосов
/ 11 июня 2010

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

Вы находитесь в процессе переизобретения инъекций зависимости.Поэтому, возможно, стоит взглянуть на Spring или Guice, чтобы помочь вам с сантехникой.(Если вы находитесь на земле Java).

Ваш тест действительно может внедрить Mocked Facebook SDK и службу базы данных, используя аргументы сеттера или конструктора.

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

0 голосов
/ 11 июня 2010

Если вы сделаете объект (ы) Facebook SDK общедоступным, ваш пользовательский объект все равно может создать его при настройке, но вы можете заменить его на фиктивный, прежде чем фактически что-либо делать с объектом User.

[Test]
public void Test()
{
    User u = new User(); // let's say that the object on User gets created in the ctor
    u.FacebookObj = new DynamicMock(typeof(FacebookSDK)).MockInstance;

    Assert.That(u.Method(), Does.Stuff, "u.Method didn't do stuff");
}
...