модульное тестирование, как далеко пройти при тестировании черного ящика? - PullRequest
1 голос
/ 04 декабря 2010

Привет,

У меня возник странный вопрос.

Я полагаю, что при тестировании кода вы вводите параметры x, y, z в функцию, вам нужно проверить вывод, чтобы убедиться, что работа была выполнена правильно. Но что, если ваш код использует шаблон фасада или связывается с «внутренним» источником, к которому вы можете обратиться, но это быстро вызовет проблемы с жесткой связью в тестовом коде.

Вот пример:

class Notifier

  def send_notification action_string
    # contact messaging server and deliver message
  end

end

class DoSpecialStuff

  def my_method
    code.. code..
    n = Notifier.new
    n.send_notification "WOOT"
  end

end

Теперь, DoSpecialStuff.my_method не имеет никакого вывода, но Notifier, который предназначен для абстрагирования от "сложности" того, куда выводится! В результате получается непроверяемая функция!

Проверяем ли мы этот код, делая это?

def test
  assert_no_execption_raised do # hurm... a test in name only?
    o = DoSpecialStuff.new
    o.my_method
  end
end

Теперь мы можем проверить, куда отправляется это сообщение, но это отнимает точку объекта фасада, мы пытаемся скрыть сложность.

Мы можем проверить вывод, но должны мы? Что правильно и правильно?

-daniel

Ответы [ 3 ]

2 голосов
/ 04 декабря 2010

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

Это похоже на то место, где тестирование взаимодействиябыло бы полезно.Макет Notifier в тесте DoSpecialStuff, чтобы вы могли быть уверены, что он использует Notifier правильно.Выше может потребоваться макет DoSpecialStuff для тестирования кода, который находится поверх него.

1 голос
/ 04 декабря 2010

для точного масштабирования ваших тестов / кода вы должны разъединить ваш код с помощью интерфейса , что значительно упрощает обновление тестов после рефакторинга, в противном случае, связывая ваш код, вы несете техническую задолженность вы могли бы потенциально добавить

  1. интерфейс для развязки кода
  2. макет для DoSpecialStuff, чтобы проверить, что разъединенный код ведет себя так, как вы ожидаете

, если выгде при написании кода на C ++ он будет выглядеть примерно так:

Код уведомителя

Notifier.h
class Notifier : public I_Notifier
{
    void _sendNotification( NotificationInstance * notification );
};

Notifier.cpp
void Notifier::_sendNotification( NotificationInstance * notification ) 
{
  //do some stuff that needs testing 
}

Интерфейс для уведомителя, это то, что добавляется в класс DoSpecialStuff для отделения кода I_Notifier.h

class I_Notifier
{
    void sendNotification( NotificationInstance * notification ) { _sendNotification(notification); }
    virtual void _sendNotification( NotificationInstance * notification )=0;
}

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

Mock_Notifier.h
struct Test_Notifier : public I_Notifier
{
  Test_Notifier() : send_notification_called(false)

  virtual void _sendNotification( NotificationInstance * instance ) 
  {
    send_notification_called = true;
  }
  bool send_notification_called;
};

Код DoSpecialStuff Обратите внимание, что этот класс теперь имеет интерфейс к классу уведомлений , который разъединяет код (поэтому нашему тесту больше не нужно включатьфактический класс, если мы не используем вызовы от него

DoSpecialStuff.cpp
class DoSpecialStuff
{
  DoSpecialStuff( I_Notifier * n ) : notifier_(n) {}
  void DoSpecialStuff::_myMethod( NotificationInstance * notification ) 
  I_Notifier * notifier_;
}

void DoSpecialStuff::_myMethod( NotificationInstance * notification ) 
{
  //do some stuff that needs testing
  n.send_notification (notification )
}

Интерфейс

I_DoSpecialStuff.h
class I_DoSpecialStuff
{
    void myMethod( NotificationInstance * notification ) { _myMethod(notification); }
    virtual void _myMethod( NotificationInstance * notification )=0;
}

Где функциональность

  • связаться с сервером обмена сообщениями и доставить сообщение

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

0 голосов
/ 04 декабря 2010

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

class DoSpecialStuff

  def my_method
    code.. code..
    send_notification        
  end

  def send_notifier
    n = Notifier.new
    n.send_notification "WOOT"
  end

end

Простите, если мой синтаксис неверенНа каком бы языке это ни было (Ruby?), я не знаком.

Теперь, чтобы проверить, расширьте DoSpecialStuff и override send_notification, чтобы он либо ничего не делал, либо вы могли убедиться, что он был вызван вашим тестом (я предполагаю, что есть какой-тобросить глобальную переменную или что-то подобное).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...