Использование одного и того же набора тестов в различных реализациях интерфейса репозитория.

StackOverflow https://stackoverflow.com/questions/81317

Вопрос

Я создавал небольшое игрушечное веб-приложение на 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, также будет представлять некоторый интерес.

Это было полезно?

Решение

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

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

Другие советы

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

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

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

то естьвот что я имел в виду:

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);
}
[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);
}

Подводя итог, можно сказать, что есть три пути:

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

2) Используйте функцию RowTest MBUnit для автоматизации этого (ответ Джона Лимджапа).Я бы также использовал здесь перечисление, например.

[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) Меньше всего набора текста для подключения нового репозитория, если это необходимо - это будет сделано в одном месте, в отличие от других.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top