NUnit тестирование приложения, а не среды или базы данных - PullRequest
1 голос
/ 28 февраля 2009

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

Позвольте мне привести пример.

Я пишу класс, который несет единоличную ответственность за извлечение строки, которая была сохранена в реестре другим приложением. Ключ хранится в HKCU \ Software \ CustomApplication \ IniPath.

Тест, который я заканчиваю писать, выглядит так:

[Test]
public void GetIniDir()
{
    RegistryReader r = new RegistryReader();
    Assert.AreEqual(@"C:\Programfiles\CustomApplication\SomeDir", r.IniDir);
}

Но проблема здесь в том, что строка @ "C: \ Programfiles \ CustomApplication \ SomeDir" действительно сейчас правильная. Завтра он может измениться на @ "C: \ Anotherdir \ SomeDir", и это внезапно нарушит мои модульные тесты, даже если код не изменился.

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

[Test]
public void GetAllCustomersCount()
{
    DAL d = new DAL();
    Assert.AreEqual(249, d.GetCustomerCount());
}

Ребята, есть ли у вас какие-либо советы по написанию тестов, которые не так сильно зависят от окружающей среды?

Ответы [ 4 ]

6 голосов
/ 28 февраля 2009

Решение этой проблемы хорошо известно: издевательство . Переведите ваш код в интерфейсы, затем разработайте поддельные классы для реализации этих интерфейсов или смоделируйте их с помощью фальшивой инфраструктуры, такой как RhinoMocks , easyMock , Moq , et. и др. Использование поддельных или фиктивных классов позволяет вам определять, что интерфейс возвращает для вашего теста, без необходимости фактически взаимодействовать с внешним объектом, таким как база данных.

Для получения дополнительной информации о насмешках через SO, попробуйте этот поиск Google: http://www.google.com/search?q=mock+site:stackoverflow.com. Вы также можете быть заинтересованы в определениях по адресу: В чем разница между подделкой, издевательством и окурком?

Кроме того, хорошие методы разработки, такие как внедрение зависимостей (как предлагает @Patrik), который позволяет отделить ваши классы от его зависимостей и избежать статических объектов, что усложняет юнит-тестирование, облегчат ваше тестирование. Использование методов TDD - там, где тесты разрабатываются первыми - поможет вам естественным образом разработать приложения, в которых используются эти принципы проектирования.

2 голосов
/ 28 февраля 2009

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

public interface IRegistry
{
    string GetCurrentUserValue(string key);
}

public class RegistryReader
{
    public RegistryReader(IRegistry registry)
    { 
        ...
        // make the dependency explicit in the constructor.
    }
}
[TestFixture]
public class RegistryReaderTests
{
    [Test]
    public void Foo_test()
    { 
        var stub = new StubRegistry();
        stub.ReturnValue = "known value";

        RegistryReader testedReader = new RegistryReader(stub);

        // test here...
    }

    public class StubRegistry
        : IRegistry
    {
        public string ReturnValue;

        public string GetCurrentUserValue(string key)
        {
            return ReturnValue;
        }
    }
}

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

0 голосов
/ 28 февраля 2009

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

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

Затем, когда какой-то другой класс должен найти начальный каталог, этот другой класс должен иметь следующий ctor:

public SomeOtherClass(IIniDirProvider iniDirProvider)
{
    this.iniDirProvider = iniDirProvider;
}

- позволяя вам пройти макет IIniDirProvider, когда вам нужно провести юнит-тест SomeOtherClass. Таким образом, ваш модульный тест не будет зависеть от наличия чего-либо в реестре.

0 голосов
/ 28 февраля 2009

Другой способ - создать отдельную базу данных для тестов.

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