модульный тест с помощью проверки работоспособности выходных данных - PullRequest
1 голос
/ 10 февраля 2012

Я часто видел тесты, в которых постоянные входы вводятся в программу, каждый проверяет выходные данные, сгенерированные на основе ожидаемых (ожидаемых) выходов, обычно через diff. Если разница принята, считается, что код прошел тест.

Вопросы

1) Это приемлемый юнит-тест?

2) Обычно входные данные модульного теста считываются из файловой системы и являются большими XML-файлы (возможно, они представляют очень большую систему). Допустимы ли юнит-тесты прикоснуться к файловой системе? Или модульный тест создаст небольшой вход на лету и передать это к тестируемому коду?

3) Как можно выполнить рефакторинг существующего кода, чтобы он мог тестироваться модулем?

Ответы [ 6 ]

2 голосов
/ 10 февраля 2012

Выходные различия

Если ваше требование состоит в том, чтобы производить продукцию с определенной степенью точности , то такие тесты абсолютно хороши. Это вы принимаете окончательное решение - «Достаточно ли хорошего результата или нет?» .

Разговор с файловой системой

Вы не хотите, чтобы ваши тесты взаимодействовали с файловой системой в терминах , полагаясь на то, что некоторые файлы существуют где-то для того, чтобы ваши тесты работали (например, считывая значения из файлов конфигурации). Это немного отличается от входных тестов resources - вы обычно можете встраивать их в свои тесты (или, по крайней мере, тестовый проект), рассматривать их как часть кодовой базы, и в дополнение к этому они обычно должны загружаться до выполнения теста. Например, при тестировании довольно больших XML-файлов целесообразно хранить их как отдельные файлы, а не как строки в файлах кода (что иногда можно сделать вместо этого).

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

Рефакторинг

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

1 голос
/ 10 февраля 2012

1) это приемлемый юнит-тест?

Это не модульный тест по определению. Модульный тест фокусируется на минимально возможном объеме кода. Ваш тест все еще может быть полезным тестом, регрессионным тестом, тестом самодокументирования, тестом TDD и т. Д. Это просто не модульный тест, хотя он может быть одинаково полезным.

2) Должны ли модульные тесты затрагивать файловую систему?

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

3) Как можно выполнить рефакторинг существующего кода, чтобы он мог тестироваться модулем?

Лучший вопрос: почему вы хотите, чтобы код был модульно тестируемым? Если вы пытаетесь изучить TDD, лучше начать с нового проекта. Если у вас есть ошибки, попробуйте написать тесты для ошибок. Если дизайн замедляет работу, то вы можете со временем выполнить рефакторинг в сторону тестируемости.

0 голосов
/ 10 февраля 2012

Вы не должны требовать рефакторинга кода для возможности выполнения модульных тестов.Модульные тесты, как следует из названия, тестируют единицу кода для системы.Лучшие модульные тесты - это небольшие, быстрые для выполнения и выполняющие только очень маленькое подмножество тестируемого фрагмента кода (например, класса).

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

Что касается доступа к файловой системе, я не вижу проблем.Некоторые модульные тесты могут требовать создания базы данных до проведения теста, проверяемого вывода, который будет трудным или затратным по времени для написания проверок в коде.

Файлы для модульного тестирования должны обрабатыватьсякак и остальной код - поставить под контроль версий.Если вы параноик, вы можете выполнить проверку в модульном тесте, например, выполнить для него MD5 и проверить жестко закодированное значение, чтобы в будущем при повторном запуске теста можно было убедиться, что данные теста не были случайно изменены.

Просто мои скромные мысли.

0 голосов
/ 10 февраля 2012

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

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

int itemsBeforeTest = SUT.Items.Count;

SUT.AddItem();

Assert.AreEqual(itemsBeforeTest + 1, SUT.Items.Count);

Если для модульных тестов требуется так много данных, что их необходимо прочитать из большого XML-файла, это не настоящий модульный тест. Модульный тест должен проверить класс в полной изоляции и смоделировать все зависимости.

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

Например, допустим, у вас есть следующий код заказа для проверки правильности цены счета:

Address billingAddress = new Address("Stationsweg 9F",
    "Groningen", "Nederland", "9726AE"); shippingAddress = new Address("Aweg 1",
    "Groningen", "Nederland", "9726AB");
Customer customer = new Customer(99, "Piet", "Klaassens",
                        30,
                        billingAddress,
                        shippingAddress);
Product product = new Product(88, "Tafel", 19.99);
Invoice invoice = new Invoice(customer);

Может быть изменено на следующее при использовании Builder

Invoice invoice = Builder<Invoice>.CreateNew()
                    .With(i => i.Product = Builder<Product>.CreateNew()
                        .With(p => p.Price = 19.99)
                        .Build())
                    .Build();

При использовании Builder гораздо проще увидеть, что важно, и ваш код также более удобен в обслуживании.

Рефакторинг кода, чтобы стать более тестируемым - это широкая тема. Все сводится к размышлению о том, «как бы я протестировал этот код?» пока вы пишете код.

Возьмите следующий пример:

public class AlarmClock
{
    public AlarmClock()
    {
        SatelliteSyncService = new SatelliteSyncService();
        HardwareClient = new HardwareClient();
    }
}

Это сложно проверить. При тестировании AlarmClock необходимо убедиться, что SatteliteSyncService и HardwareClient работают нормально.

Это изменение в конструкторе облегчает тестирование:

public AlarmClock(IHardwareClient hardwareClient, ISatelliteSyncService satelliteSyncService)
{
    SatelliteSyncService = satelliteSyncService;
    HardwareClient = hardwareClient;
}

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

Действительно хорошее введение в написание тестируемого кода можно найти здесь .

0 голосов
/ 10 февраля 2012
  1. Это приемлемый модульный тест.

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

  3. Наличие тестов черного ящика - отличное начало, вы можете реорганизовать существующий код и использовать текущие тесты, чтобы убедиться, что он все еще работает (в зависимости отна качество тестов).Вот небольшой блог о рефакторинге для тестируемости: http://www.beletsky.net/2011/02/refactoring-to-testability.html

0 голосов
/ 10 февраля 2012

Обращаясь только к 3-му вопросу.Это чрезвычайно сложно.Вам действительно нужно писать тесты одновременно с написанием кода или раньше.Попытка поставить тесты на существующую кодовую базу - это кошмар, и зачастую продуктивнее отбрасывать код и начинать заново.

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