Модульное тестирование ... как его улучшить - PullRequest
7 голосов
/ 22 ноября 2011

Я хочу протестировать фрагмент кода, который возвращает объект.

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

[Test]
public void GetMyObjectFromLog()
{
     string _xmlFilePath = @"C:\XmlFile.xml";
     MyObjectParser _myObjectParser = new MyObjectParser();
     MyObject _mockMyObject = new MyObject
                              {
                                   Title = "obj",
                                   Name = "objName"
                              }
     MyObject _myObject = _myObjectParser.GetMyObjectFromLog(_xmlFilePath);

     Assert.AreEqual(_mockMyObject , _myObject);
}

Этот тест не работает, поскольку MyObject не переопределяет метод Equals, и я не хочу переопределять метод Equals только для целей теста.

Поэтому я переписываю тест следующим образом:

[Test]
public void GetMyObjectFromLog()
{
     string _xmlFilePath = @"C:\XmlFile.xml";
     MyObjectParser _myObjectParser = new MyObjectParser();
     MyObject _myObject = _myObjectParser.GetMyObjectFromLog(_xmlFilePath);

     Assert.AreEqual("obj", _myObject.Title);
     Assert.AreEqual("objName", _myObject.Name);
}

Хорошо, это работает ... но подходит ли этот тест? Более того, существует зависимость от файла.

Уместно ли использовать Mock Framework вместо? И как им пользоваться?

Спасибо!

Ответы [ 5 ]

5 голосов
/ 22 ноября 2011

Прежде всего, метод внутри парсера должен называться «Parse» вместо «Get».

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

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

[Test]
public void ParsesObjectFromXml()
{
     string xmlInput = " ... ";
     MyObjectXmlParser parser = new MyObjectXmlParser();
     MyObject expected = new MyObject() {Title = "obj", Name="objName"};

     AssertMyObjectsAreEqual(expected, parser.Parse(xmlInput));
}

private bool AssertMyObjectsAreEqual(MyObject expected, MyObject actual)
{
     Assert.AreEqual(expected.Title, actual.Title);
     Assert.AreEqual(expected.Name, actual.Name);
}

Теперь и ваш класс, и ваш тест более понятны и несут только одну ответственность.

2 голосов
/ 22 ноября 2011

Об этой зависимости от файла (который даже отсутствует в проекте!):

Вы можете переопределить это _myObjectParser.GetMyObjectFromLog, чтобы также принять поток. Затем вы можете добавить этот XML-файл как встроенный ресурс и прочитать его из сборки.

1 голос
/ 22 ноября 2011

Да, это выглядит нормально.Однако вы можете переопределить Equals в своем тестовом проекте, если он не нужен в вашем основном проекте.Например, я обычно создаю классы, которые продвигают частные и защищенные классы в публичные.Таким образом, вы можете расширить свой основной проект для простого тестирования в своем тестовом проекте.

С файловой зависимостью все в порядке, просто поместите этот файл в свой проект набора тестов.

1 голос
/ 22 ноября 2011

Абсолютно нормально иметь ссылку на файл. Обычно существует два вида тестов: модульные тесты и интеграционные тесты.

Интеграционные тесты всегда взаимодействуют с каким-либо файлом / базой данных или чем-либо еще. В вашем случае вы могли бы улучшить тест, посмеявшись над _myObjectParser.GetMyObjectFromLog.

Вы можете написать макет самостоятельно или использовать фреймворк, например, rhinomocks. Очень хорошая книга для монахинь и носорогов - «Искусство модульного тестирования».

Лучшая тестируемая версия GetMyObjectFromLog может выглядеть следующим образом:

public MyObject GetMyObjectFromLog(IMyXmlReader reader)
{
    var xmlData = reader.GetData();
    //make your object here
    var obj = new MyObject(xmlData);
    return obj;  
}

Затем вы можете реализовать 2 новых класса, которые реализуют интерфейс IMyXmlReader, один, который читает из файла для вашего производительного кода, и тот, который всегда возвращает одну и ту же строку в GetData ().

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

Понял? Извините за мой английский:)

0 голосов
/ 22 ноября 2011

Цель модульного тестирования состоит в том, чтобы проверить логику кода. Ваш выше тест выполняет две вещи: 1. Логика 2. Поиск

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

Решение: Создайте файл на ходу, но не создавайте физический файл, создайте файл в памяти, а затем вставьте объект, который необходимо сопоставить, затем сопоставьте объект.

Таким образом, этот тест никогда не сломается.

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