Эти юнит-тесты в порядке? - PullRequest
       17

Эти юнит-тесты в порядке?

4 голосов
/ 07 апреля 2009

Я пытаюсь понять разработку, управляемую тестами, и мне интересно, подходят ли эти юнит-тесты. У меня есть интерфейс, который выглядит так:

public interface IEntryRepository
{
    IEnumerable<Entry> FetchAll();
    Entry Fetch(int id);
    void Add(Entry entry);
    void Delete(Entry entry);
}

А затем этот класс, который реализует этот интерфейс:

public class EntryRepository : IEntryRepository
{
    public List<Entry> Entries {get; set; }

    public EntryRepository()
    {
        Entries = new List<Entry>();
    }

    public IEnumerable<Entry> FetchAll()
    {
        throw new NotImplementedException();
    }

    public Entry Fetch(int id)
    {
        return Entries.SingleOrDefault(e => e.ID == id);
    }

    public void Add(Entry entry)
    {
        Entries.Add(entry);
    }

    public void Delete(Entry entry)
    {
        Entries.Remove(entry);
    }
}

Это написанные мной модульные тесты, они в порядке, или я должен сделать что-то другое? Должен ли я издеваться над EntryRepository?

[TestClass]
public class EntryRepositoryTests
{
    private EntryRepository rep;

    public EntryRepositoryTests()
    {
        rep = new EntryRepository();
    }

    [TestMethod]
    public void TestAddEntry()
    {
        Entry e = new Entry { ID = 1, Date = DateTime.Now, Task = "Testing" };
        rep.Add(e);

        Assert.AreEqual(1, rep.Entries.Count, "Add entry failed");
    }

    [TestMethod]
    public void TestRemoveEntry()
    {
        Entry e = new Entry { ID = 1, Date = DateTime.Now, Task = "Testing" };
        rep.Add(e);

        rep.Delete(e);
        Assert.AreEqual(null, rep.Entries.SingleOrDefault(i => i.ID == 1), "Delete entry failed");
    }

    [TestMethod]
    public void TestFetchEntry()
    {
        Entry e = new Entry { ID = 2, Date = DateTime.Now, Task = "Testing" };
        rep.Add(e);

        Assert.AreEqual(2, rep.Fetch(2).ID, "Fetch entry failed");
    }
}

Спасибо!

Ответы [ 8 ]

7 голосов
/ 07 апреля 2009

Просто с макушки головы ...

Хотя ваше тестирование add действительно проверяет только фреймворк:

  • Вы добавили 1 предмет, это хорошо.
  • как насчет добавления множества предметов (Я имею в виду, смешные суммы - для какого значения из n записей сбой контейнера добавляет?)
  • как насчет добавления никаких предметов? (пустая запись)
  • если вы добавляете элементы в список, они находятся в определенном порядке? они должны быть?

аналогично вашему извлечению:

  • что происходит в вашей выборке (x), если x> rep.Count?
  • что будет, если x <0? </li>
  • что будет, если представитель пуст?
  • соответствует ли x требованиям к производительности (что это алгоритмическое сложность? это в пределах диапазона, когда есть только одна запись и когда там смехотворно большое количество записей?

В книге есть хороший контрольный список Прагматическое модульное тестирование (хорошая книга, настоятельно рекомендуется)

  • Правильны ли результаты?
  • все ли граничные условия ПРАВИЛЬНЫ
    • Соответствует ожидаемому формату
    • Заказан правильно
    • В разумных пределах
    • Ссылка ли это на какие-либо внешние зависимости
    • Правильно ли количество элементов? (правильное количество значений)
    • завершает ли он в правильном количестве времени (реального или относительного)
  • Можете ли вы проверить обратные отношения
  • Можете ли вы проверить результаты другим проверенным методом
  • Можете ли вы форсировать условия ошибки
  • Находятся ли характеристики в определенных пределах
4 голосов
/ 07 апреля 2009

Вот несколько мыслей:

Positive

  • Вы юнит тестирование!
  • Вы следуете соглашению Arrange, Act, Assert

Отрицательный

  • Где тест для удаления записи, когда ее нет?
  • Где тест для извлечения записи, когда ее нет?
  • Что должно произойти, когда вы добавляете две записи и удаляете одну? Какой из них должен быть оставлен?
  • Должен Entries быть публичным. Тот факт, что один из ваших утверждений вызывает rep.Entries.SingleOrDefault, подсказывает мне, что вы неправильно строите класс.
  • Ваше тестовое наименование немного расплывчато; как правило, хорошим примером для подражания является: {MethodName}_{Context}_{Expected Behavior}, который удаляет избыточность «тестовой» избыточности.

Как новичок в TDD, я нашел книгу Разработка через тестирование на примере , которая очень помогла. Во-вторых, у Роя Ошерова есть несколько хороших тестовых обзоров видеоуроков, посмотрите их.

1 голос
/ 07 апреля 2009

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

Но я чувствую, что ваши юнит-тесты в значительной степени избыточны. Многие из ваших методов просты, так как ваш метод AddEntry является просто вызовом базового метода List Add. Вы не тестируете свой код, вы тестируете библиотеку Java.

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

0 голосов
/ 07 апреля 2009

Для начинающих TDD и для этого конкретного класса, ваши тесты в порядке. +1 за усилие.

Опубликуйте еще один вопрос, как только вы попадете в более сложные сценарии, включающие внедрение зависимостей и насмешки. Здесь вещи становятся действительно интересными;).

0 голосов
/ 07 апреля 2009

Вы не должны насмехаться над IEntryRepository, поскольку реализующий класс является тестируемым классом. Вы можете захотеть смоделировать List<Entry> и внедрить его, а затем просто проверить, что методы, которые вы вызываете через открытый интерфейс, вызываются соответствующим образом. Это было бы просто альтернативой тому, как вы это реализовали, и не обязательно лучше - если только вы не хотите, чтобы класс вводил свой класс поддержки, и в этом случае написание тестов таким способом вызовет такое поведение.

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

0 голосов
/ 07 апреля 2009

Это выглядит как хорошее начало, но вы должны попытаться как можно больше проверить «пограничные» случаи. Подумайте о том, что может привести к сбою ваших методов. Будет ли допустимой передача нулевой записи в Add или Delete? Попробуйте написать тесты, которые используют все возможные пути кода. Написание тестов таким образом сделает ваш набор тестов более полезным в будущем, если вы внесете какие-либо изменения в код.

Кроме того, для каждого метода тестирования полезно оставлять тестовый объект в том же состоянии, в котором он был вызван. Я заметил, что ваш метод TestFetchEntry добавляет элемент в EntryRepository, но никогда не удаляет его. Отсутствие влияния каждого метода на состояние тестового объекта облегчает выполнение серии тестов.

0 голосов
/ 07 апреля 2009

Кажется, хорошо, как это. Мне лично нравится давать своим тестам немного более описательные имена, но это больше личное предпочтение.

Вы можете использовать mocking для зависимостей класса, который вы тестируете, EntryRepository - это тестируемый класс, так что вам не нужно это делать, иначе вы в конечном итоге протестируете реализацию mock вместо класса.

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

0 голосов
/ 07 апреля 2009

Хорошо выглядит в целом. Вы должны использовать транзакции (или создать новый экземпляр репозитория в TestInitialize), чтобы убедиться, что тесты полностью изолированы.

Также используйте более описательные методы тестирования, такие как When_a_new_entity_is_added_to_a_EntryRepository_the_total_count_of_objects_should_get_incremented

...