Использование одного и того же набора тестов в различных реализациях интерфейса репозитория - PullRequest
3 голосов
/ 17 сентября 2008

Я делал небольшое игрушечное веб-приложение на C # по аналогии с магазином Роба Коннери Asp.net MVC.

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

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

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

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

1010 *, например *

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

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

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

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

Ответы [ 5 ]

3 голосов
/ 17 сентября 2008

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

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

3 голосов
/ 17 сентября 2008

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

[RowTest]
[Row(new ThisRepository())]
[Row(new ThatRepository())]
Public void GetFoo_NotNull_Test(IFooRepository repository)
{
   var results = repository.GetFoo();
   Assert.IsNotNull(results);
}
1 голос
/ 17 сентября 2008

Если у вас есть 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 голосов
/ 17 сентября 2008

Подводя итог, можно пойти тремя путями:

1) Сделайте тесты одним лайнером, который вызывает общие методы (ответ Рика, также Халлгрима)

2) Используйте функцию RowTest MBUnit, чтобы автоматизировать это (ответ 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) Используйте базовый класс, ответьте по белугабобу
Я сделал образец на основе этой идеи

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

Недостатки:
1) Меньшая гибкость, чтобы не запускать тесты с провайдером в случае необходимости
2) Труднее читать.

0 голосов
/ 17 сентября 2008
[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);
}
...