2008-09-17 8 views
3

Я делаю небольшое игрушечное веб-приложение на C# по строкам магазина Asp.net MVC Роба Коннери.Использование того же набора тестов для различных реализаций интерфейса репозитория

Я считаю, что у меня есть интерфейс хранилища, назовем его IFooRepository, с методами, скажем

IQueryable<Foo> GetFoo(); 
void PersistFoo(Foo foo); 

И у меня есть три реализации этого: ISqlFooRepository, IFileFooRepostory и IMockFooRepository.

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

например.

[TestMethod] 
Public void GetFoo_NotNull_Test() 
{ 
    IFooRepository repository = GetRepository(); 
    var results = repository. GetFoo(); 
    Assert.IsNotNull(results); 
} 

Я хочу, чтобы этот метод испытания для запуска в три раза, с некоторыми изменениями в окружающей среде, что позволяет ему получить три различных вида хранилища. В настоящее время у меня есть три класса тестов с вырезанием и вставкой, которые различаются только в реализации частного вспомогательного метода IFooRepository GetRepository(); Очевидно, это вонючий.

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

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

ответ

3

Создать абстрактный класс, который содержит конкретные варианты тестов и абстрактный метод, который возвращает GetRepository IFooRepository. Создайте три класса, которые вытекают из абстрактного класса, каждый из которых реализует GetRepository таким образом, который возвращает соответствующую реализацию IFooRepository. Добавьте все три класса в свой тестовый набор, и вы готовы к работе.

Для выборочного запуска тестов для некоторых поставщиков, а не для других, рассмотрите возможность использования атрибута MbUnit '[FixtureCategory] для категоризации ваших тестов - предлагаемые категории - «быстрые», «медленные», «db», «важные» и «неважная» (Последние две шутки - честное слово!)

3

В MbUnit вы можете использовать атрибут RowTest для указания параметров в своем тесте.

[RowTest] 
[Row(new ThisRepository())] 
[Row(new ThatRepository())] 
Public void GetFoo_NotNull_Test(IFooRepository repository) 
{ 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 
+0

Хороший ответ, но если бы я использовал NUnit и RowTest, я бы использовал перечисление в rowtest, как в ответе Рика. По сути, он делает RowTest трудным путем. – Anthony 2008-09-17 10:24:26

+0

Ну, я отправил свой ответ впереди Рика. поэтому я понятия не имею, что он делает. – 2008-09-17 15:12:09

1

Если у вас есть 3 копии и вставленные методы испытаний, вы должны быть в состоянии реорганизовать (метод экстракта) его, чтобы избавиться от дублирования.

т.е. это то, что я имел в виду:

private IRepository GetRepository(RepositoryType repositoryType) 
{ 
    switch (repositoryType) 
    { 
      case RepositoryType.Sql: 
      // return a SQL repository 
      case RepositoryType.Mock: 
      // return a mock repository 
      // etc 
    } 
} 

private void TestGetFooNotNull(RepositoryType repositoryType) 
{ 
    IFooRepository repository = GetRepository(repositoryType); 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 

[TestMethod] 
public void GetFoo_NotNull_Sql() 
{ 
    this.TestGetFooNotNull(RepositoryType.Sql); 
} 

[TestMethod] 
public void GetFoo_NotNull_File() 
{ 
    this.TestGetFooNotNull(RepositoryType.File); 
} 

[TestMethod] 
public void GetFoo_NotNull_Mock() 
{ 
    this.TestGetFooNotNull(RepositoryType.Mock); 
} 
0
[TestMethod] 
public void GetFoo_NotNull_Test_ForFile() 
{ 
    GetFoo_NotNull(new FileRepository().GetRepository()); 
} 

[TestMethod] 
public void GetFoo_NotNull_Test_ForSql() 
{ 
    GetFoo_NotNull(new SqlRepository().GetRepository()); 
} 


private void GetFoo_NotNull(IFooRepository repository) 
{ 
    var results = repository. GetFoo(); 
    Assert.IsNotNull(results); 
} 
0

Подводя итог, есть три способа пойти:

1) Сделайте испытание один лайнерами, которые требуют до общих методов (ответ Rick, также Hallgrim)

2) Используйте строку MBUnit's Тест-функция для автоматизации этого (ответ Jon Limjap). Я также использовал бы перечисление здесь, например.

[RowTest] 
[Row(RepositoryType.Sql)] 
[Row(RepositoryType.Mock)] 
public void TestGetFooNotNull(RepositoryType repositoryType) 
{ 
    IFooRepository repository = GetRepository(repositoryType); 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 

3) Используйте базовый класс, ответ на belugabob
Я сделал выборку на основе этой идеи

public abstract class TestBase 
{ 
    protected int foo = 0; 

    [TestMethod] 
    public void TestUnderTen() 
    { 
     Assert.IsTrue(foo < 10); 
    } 

    [TestMethod] 
    public void TestOver2() 
    { 
     Assert.IsTrue(foo > 2); 
    } 
} 

[TestClass] 
public class TestA: TestBase 
{ 
    public TestA() 
    { 
     foo = 4; 
    } 
} 

[TestClass] 
public class TestB: TestBase 
{ 
    public TestB() 
    { 
     foo = 6; 
    } 
} 

Это производит четыре проходящие испытания в двух тестовых классов.
расквитаться из 3 являются:
1) Наименее дополнительный код, минимум обслуживания
2) Наименее типирование, чтобы подключить новое хранилище в случае необходимости - это будет сделано в одном месте, в отличие от других.

Downsides являются:
1) Менее гибкость не запускать тест против поставщика в случае необходимости
2) Тяжелее читать.