Отверстие в знаниях относительно TDD - PullRequest
2 голосов
/ 22 января 2012

Я составил список (так называемый «список тестов» в Test-Driven Development by Example), из которого я выберу тест для реализации.

Итак, я запускаю Visual Studio, создаю новое решение, добавить проект для модульных тестов, а затем ... Мне нужно придумать класс, в который я добавлю метод теста для теста, который я выбрал из списка.

Вот где я застрял,Как мне узнать, какой класс мне нужен, как его назвать и как узнать, является ли он правильным?Об этом нужно подумать заранее?

Ответы [ 4 ]

3 голосов
/ 22 января 2012

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

РЕДАКТИРОВАТЬ В ОТВЕТЕ НА КОММЕНТАРИЙ

Нет, не случайное имя.Вам нужно выполнить определенное количество дизайна заранее.Я часто начинаю с того, что придумываю ключевые типы, которые, по моему мнению, понадобятся моему решению.Затем я запускаю тестовый класс (например, FooTest), в котором я пишу тест для того, что я хочу, чтобы Foo делал.Я использую процесс написания теста для написания интерфейса.Resharper отлично подходит для этого, так как я могу ссылаться на типы и методы, которые еще не существуют, и Resharper создает их:

[TestFixture]
public class FooTest
{
    [Test]
    public void Bar()
    {
        var foo = (IFoo)null;  //At this point I use Resharper to create IFoo interface

        Assert.IsTrue(foo.Bar()); //At this point I use Resharper to create bool IFoo.Bar();
    }
}

, очевидно, что вышеприведенное не получится с нулевым ссылочным кодом, но у меня есть тест иУ меня есть интерфейс с методом.Я могу продолжать следовать этому процессу, чтобы смоделировать свое решение, пока не достигну точки, когда я буду готов разработать конкретную реализацию.Следуя этому процессу, я сосредоточусь на интерфейсе и взаимодействии между типами, а не на реализации этих типов.После того, как я собрал Foo, я просто изменил вышеприведенное значение на var foo = new Foo(); и сделал все тесты зеленымиЭтот процесс также означает, что у меня есть интерфейс для каждого класса, который важен при написании модульных тестов, так как я могу легко смоделировать зависимости с помощью динамической фиктивной библиотеки, например MOQ .

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

Что делает ваша система?Вы можете начать там.

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

Давайте создадим тест:

public class TransactionSummarizationTest {
    @Test
    public void summarizesAnEmptyDocument() {
        TransactionSummarization summarizer = new TransactionSummarization();
        Summary s = summarizer.summarizeTransactionsIn(new Scanner());
        assertEquals(0.00, s.debits, 0.0);
        assertEquals(0.00, s.credits, 0.0);
    }

Поскольку классы TransactionSummarization и Summary еще не существуют, вы создаете их сейчас.Они будут выглядеть так:

TransactionSummarization.java

public class TransactionSummarization {
    public Summary summarizeTransactionsIn(Scanner transactionList) {
        return null;
    }
}

Summary.java

public class Summary {
    public double debits;
    public double credits;
}

Теперь, когда вы позаботились обо всех ошибках компиляции, которые выможет запустить тест.Это не удастся с NullPointerException из-за пустой реализации метода summarizeTransactionsIn.Возвращает сводный экземпляр из метода, и он проходит.

public Summary summarizeTransactionsIn(Scanner transactionList) {
    return new Summary();
}

Запустите тест снова и он пройдет.

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

@Test
public void summarizesDebit() {
    TransactionSummarization summarizer = new TransactionSummarization();
    Summary s = summarizer.summarizeTransactionsIn(new Scanner("01/01/12,DB,1.00"));
    assertEquals(1.00, s.debits, 0.0);
    assertEquals(0.00, s.credits, 0.0);
}

После запуска теста мы должны увидеть его неудачным, потому что мы не накапливаем значения, просто возвращаем новую Summary

public Summary summarizeTransactionsIn(Scanner transactionList) {
    String currentLine = transactionList.nextLine();
    txAmount = currentLine.split(",")[2];
    double amount = Double.parseDouble(txAmount);
    return new Summary(amount);
}

После исправления ошибки компиляции в Summary и реализации конструктора ваши тесты должны пройти снова.Какой следующий тест?Чему мы можем научиться?Что ж, мне любопытно, что происходит с дебетом / кредитом, так что давайте сделаем это дальше.

@Test
public void summarizesCredit() {
    TransactionSummarization summarizer = new TransactionSummarization();
    Summary s = summarizer.summarizeTransactionsIn(new Scanner("01/01/12,CR,1.00"));
    assertEquals(0.00, s.debits, 0.0);
    assertEquals(1.00, s.credits, 0.0);
}

Запустите этот тест, мы увидим, что он не пройден, потому что дебеты составляют 1,00, а кредиты - 0,0.Точно противоположное тому, что мы хотели, но это вполне ожидаемо, потому что мы никак не исследовали тип транзакции.Давайте сделаем это сейчас.

public Summary summarizeTransactionsIn(Scanner transactionList) {
    double debits = 0.0;
    double credits = 0.0;

    String currentLine = transactionList.nextLine();
    String[] data = currentLine.split(",");
    double amount = Double.parseDouble(data[2]);

    if("DB".equals(data[1]))
        debits += amount;

    if("CR".equals(data[1]))
        credits += amount;

    return new Summary(debits, credits);
}

Теперь все тесты пройдены, и мы можем перейти к следующему тесту.Что теперь?Я думаю, что обработка только одной строки в файле не сильно нам поможет, если мы хотим, чтобы этот проект был успешным.Как насчет обработки нескольких записей одновременно?Давайте напишем тест!

@Test
public void summarizesDebitsAndCredits() {
    String transactions = "01/01/12,CR,1.75\\n" +
                          "01/02/12,DB,3.00\\n" +
                          "01/02/12,DB,2.50\\n" +
                          "01/02/12,CR,1.25";
    TransactionSummarization summarizer = new TransactionSummarization();
    Summary s = summarizer.summarizeTransactionsIn(new Scanner(transactions));
    assertEquals(5.50, s.debits, 0.0);
    assertEquals(3.00, s.credits, 0.0);
}

Теперь, запустив все наши тесты, мы увидим, что этот тест не прошел предсказуемым образом.Это говорит нам о том, что наши дебеты равны 0,00, а кредиты - 1,75, поскольку мы обработали только первую запись.

Давайте исправим это сейчас.Простой цикл while, и мы должны вернуться к делу:

public Summary summarizeTransactionsIn(Scanner transactionList) {
    double debits = 0.0;
    double credits = 0.0;

    while(transactionList.hasLine()) {
        String currentLine = transactionList.nextLine();
        String[] data = currentLine.split(",");
        double amount = Double.parseDouble(data[2]);

        if("DB".equals(data[1]))
            debits += amount;

        if("CR".equals(data[1]))
            credits += amount;
    }
    return new Summary(debits, credits);
}

Все тесты пройдены, а остальное я оставлю на ваше усмотрение.Некоторые вещи, о которых стоит подумать, это крайние случаи, такие как файл, содержащий смешанный регистр, например, «cr» против «CR», или неверные / отсутствующие данные и т. Д.

Кроме того, я понял, что после того, как все это напечатал,что вы имели в виду C #.К сожалению, я сделал это на Java, и мне лень конвертировать его в C #, но я надеюсь, что это все равно поможет.: -)

Спасибо!

Брэндон

0 голосов
/ 22 января 2012

Хорошей идеей было бы начать думать об этом с точки зрения вашего домена , а не какого-то смутного теста .Например, вам нужно разработать Foo с функциональностью foo1 и foo2 .

Таким образом, вы создаете тестовый класс с именем FooTest с помощью foo1Test и foo2Test.Первоначально эти тесты не пройдут, и вы просто работаете, чтобы заставить их пройти.

0 голосов
/ 22 января 2012

Об этом нужно подумать заранее?

Заранее.По этой причине она называется Test Driven Develop ...
Вам нужно спроектировать рабочий процесс до того, как начнет внедряться.

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