Реализация объекта в тестовых классах - PullRequest
8 голосов
/ 04 июня 2010

Я использую MS UnitTesting и пытаюсь найти способ написать свои первые модульные тесты. Кажется, все мои модульные тесты начинаются с создания одних и тех же нескольких объектов ...

[TestMethod]
CanCreateOrder()
{
    <create an order>
    ...
}


[TestMethod]
CanSetOrderDeliveryAddress()
{
    <create an order>
    <create an address>
    order.DeliveryAddress = address;
    ...
}

[TestMethod]
CanDispatchAnOrder()
{
    <create an order>
    <create an address>
    order.DeliveryAddress = address;
    order.Dispatch();
    ...
}

... и т.д.

Это нормально, или я неправильно понял? Я вроде думал, что каждый тест должен был быть независимым, но как можно было бы протестировать Dispatch() без того, чтобы неявно полагаться на то, что CreateOrder и SetDeliveryAddress уже пройдены?

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

Ответы [ 3 ]

8 голосов
/ 04 июня 2010

Вы, кажется, на правильном пути для меня. Во многих модульных тестах вам необходимо установить состояние объекта, который вы тестируете, чтобы иметь возможность тестировать определенную часть поведения. Это поможет сгруппировать тесты в классах, когда тестируемое поведение требует аналогичной настройки, а также использовать методы [TestInitialize], чтобы уменьшить дублирование этой настройки.

например:

[TestClass]
public class WhenReadyToDispatch{

private Order order;

[TestInitialize]
public void Initialize
{
     order = <create an order>
    order.DeliveryAddress = <create an address>
}

[TestMethod]
CanChangeOrderDeliveryAddress()
{

    order.DeliveryAddress = address;
}

[TestMethod]
CanDispatchAnOrder()
{
    order.Dispatch();
}

}

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

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

Ваш первый вопрос связан с издевательством и заглушкой , когда вы создаете поддельный заказ и адрес, созданные из интерфейса каждого класса. Вот так вы можете просто протестировать метод Dispatch.

Затем вы можете утверждать, что метод отправки правильно сделал, проверив, что случилось с вашими поддельными (заглушками / насмешками) объектами.

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

1 голос
/ 04 июня 2010

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

Если Order.DeliveryAddress - просто простой метод получения / установки, не беспокойтесь о его тестировании. Это все равно что пытаться доказать, что C # ведет себя как следует. Есть небольшое преимущество для этого. И наоборот, если ваш диспетчерский тест полагается на то, что это свойство находится в рабочем состоянии, на самом деле это не зависимость.

Однако, если Order.DeliveryAddress выполняет логику, например, гарантирует, что адрес может изменяться только, например, для не отправленных заказов, то это более сложно. Вы, вероятно, не хотите пытаться отправить весь заказ, просто чтобы проверить, что Order.DeliveryAddress больше не может быть изменен впоследствии.

Вызов принципа единой ответственности (см. Здесь 1 и 2 ), что бы класс Order теперь делал слишком много. Это и отправка заказов, и обеспечение целостности состояния объекта данных заказа. В этом случае вы, вероятно, захотите разделить диспетчерские функции на DispatcherService, который просто принимает заказ и отправляет его, устанавливая флаг IsDispatched для заказа в процессе.

Затем вы можете проверить поведение DeliveryAddress, просто установив соответствующее свойство IsDispatched.

Третий подход (который является своего рода обманом, но хорошо работает в ситуациях, когда вы пытаетесь провести некоторое тестирование над устаревшими объектами), заключается в создании подкласса Order для создания класса TestableOrder, который предоставляет тестовому устройству возможность работать с внутреннее состояние класса. Другими словами, он может предоставить метод MarkAsDispatched (), который установит внутренний флаг IsDispatched классов и, таким образом, позволит вам проверить, что DeliveryAddress можно установить только до того, как он будет помечен как отправленный.

Надеюсь, это поможет.

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