2009-02-28 5 views
1

Я хочу лучше использовать NUnit для тестирования приложений, которые я пишу, но часто обнаруживаю, что тестируемые модули, которые я пишу, имеют прямую ссылку на среду или базовую базу данных на вместо этого.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()); 
} 

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

ответ

6

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

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

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

0

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

+1

Но тогда вы не являетесь модульным тестированием, вы являетесь интеграционным тестированием. –

2

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

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

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

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

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

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

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