TDD: помощь в написании тестируемого класса - PullRequest
6 голосов
/ 23 января 2009

У меня небольшое приложение, и я хотел попробовать разработать его с использованием TDD. Я никогда не использовал TDD и даже не знал, что это такое, пока не нашел ASP.NET-MVC. (Мое первое приложение MVC имело модульные тесты, но они были хрупкими, сильно связаны, занимали слишком много времени и были заброшены - я пришел изучать модульные тесты! = TDD).

Фон в приложении:

У меня есть текстовый дамп заказа на покупку, считанный в виде строки. Мне нужно проанализировать текст и вернуть новый номер заказа на покупку, новый номер позиции, старый номер заказа на покупку, старый номер строки заказа на покупку. Довольно просто.

Сейчас я работаю только над новыми деталями заказа на покупку (номер / строка), и у меня есть такая модель:

public class PurchaseOrder
{
    public string NewNumber {get; private set;}
    public string NewLine {get; private set;}

    new public PurchaseOrder(string purchaseOrderText)
    {
        NewNumber = GetNewNumber(purchaseOrderText);
        NewLine = GetNewLine(purchaseOrderText);
    }

    // ... definition of GetNewNumber / GetNewLine ...
    //  both return null if they can't parse the text
}

Теперь я хочу добавить метод «IsValid», который должен быть истинным, только если «NewNumber» и «NewLine» не равны NULL. Поэтому я хочу проверить это как:

public void Purchase_Order_Is_Valid_When_New_Purchase_Order_Number_And_Line_Number_Are_Not_Null()
{
    PurchaseOrder order = new PurchaseOrder()
    {
        NewNumber = "123456",
        NewLine = "001"
    };

    Assert.IsTrue(order.IsValid);
}

Это достаточно просто, но кажется плохим компромиссом, позволяющим использовать открытые сеттеры и конструктор без параметров. Таким образом, альтернатива состоит в том, чтобы ввести значение «purchaseOrderText» в конструкторе, но затем я также тестирую код для «GetNewNumber» и «GetNewLine».

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

Ответы [ 5 ]

8 голосов
/ 23 января 2009

Одно из решений заключается в том, чтобы конструктор не выполнял работу:

public class PurchaseOrder
{
    public PurchaseOrder(string newNumber, string newLine)
    {
        NewNumber = newNumber;
        NewLine = newLine;
    }
    // ...
}

Тогда тестирование будет простым и изолированным - вы не тестируете GetNewNumber и GetNewLine одновременно.

Чтобы помочь с использованием PurchaseOrder, вы можете создать фабричный метод, который соединит его:

public static PurchaseOrder CreatePurchaseOrder(string purchaseOrderText)
{
   return new PurchaseOrder(
     GetNewNumber(purchaseOrderText),
     GetNewLine(purchaseOrderText));
}
2 голосов
/ 23 января 2009

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

В вашем основном проекте поместите что-то вроде этого;

[assembly: InternalsVisibleTo( "UnitTests" )]

Где UnitTests - это имя вашей тестовой сборки.

1 голос
/ 23 января 2009

Создайте частный метод доступа для своего класса в тестовом проекте, а затем используйте метод доступа, чтобы установить свойства для вашего теста. В VS вы можете создавать частные средства доступа, щелкнув правой кнопкой мыши в классе, выбрав «Создать частный метод доступа» и выбрав тестовый проект. В своем тесте вы затем используете его так:

public void NameOfTest()
{
    PurchaseOrder_Accessor order = new PurchaseOrder_Accessor();
    order.NewNumber = "123456";
    order.NewLine = "001";
    Assert.IsTrue(order.IsValid);
}

Если у вас есть конструктор по умолчанию, вы можете сделать:

public void NameOfTest()
{
    PurchaseOrder order = new PurchaseOrder()
    PurchaseOrder_Accessor accessor =
                new PurchaseOrder_Accessor( new PrivateObject(order) );
    accessor.NewNumber = "123456";
    accessor.NewLine = "001";
    Assert.IsTrue(order.IsValid);
}
0 голосов
/ 23 января 2009

немного оффтопа:)

Используя TDD, сначала нужно написать свои юнит-тесты, а затем написать достаточно кода, чтобы пройти тест, а затем выполнить рефакторинг. Написав сначала свои тесты, вы должны точно знать, что и, возможно, как программировать, чтобы сделать ваш код тестируемым!

только мои 2 цента ...

0 голосов
/ 23 января 2009

Я бы, вероятно, создал новый конструктор public PurchaseOrder (строка newNumber, строка newLine). ИМО, тебе, вероятно, все равно это понадобится.

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