Usando o mesmo conjunto de testes em várias implementações de uma interface de repositório

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

Pergunta

Tenho feito um pequeno aplicativo da web de brinquedo em C # nos moldes da vitrine Asp.net MVC de Rob Connery.

Acho que tenho uma interface de repositório, chamo-a de IFooRepository, com métodos, digamos

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

E eu tenho três implementações disso:ISqlFooRepository, IFileFooRepostory e IMockFooRepository.

Eu também tenho alguns casos de teste.O que eu gostaria de fazer, e ainda não descobri como fazer, é executar os mesmos casos de teste em cada uma dessas três implementações e ter uma marca verde para cada teste aprovado em cada tipo de interface.

por exemplo.

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

Quero que este método de teste seja executado três vezes, com alguma variação no ambiente que permita obter três tipos diferentes de repositório.Atualmente tenho três classes de teste recortadas e coladas que diferem apenas na implementação do método auxiliar privado IFooRepository GetRepository();Obviamente, isso é fedorento.

Porém, não posso simplesmente remover a duplicação consolidando os métodos recortados e colados, pois eles precisam estar presentes, públicos e marcados como teste para que o teste seja executado.

Estou usando a estrutura de testes da Microsoft e prefiro continuar com ela, se puder.Mas uma sugestão de como fazer isso, digamos, no MBUnit também seria de algum interesse.

Foi útil?

Solução

Crie uma classe abstrata que contenha versões concretas dos testes e um método GetRepository abstrato que retorne IFooRepository.Crie três classes derivadas da classe abstrata, cada uma implementando GetRepository de uma forma que retorne a implementação apropriada de IFooRepository.Adicione todas as três classes ao seu conjunto de testes e você estará pronto para começar.

Para poder executar seletivamente os testes para alguns provedores e não para outros, considere usar o atributo MbUnit '[FixtureCategory]' para categorizar seus testes - as categorias sugeridas são 'rápido' 'lento' 'db' 'importante' e 'sem importância' ( Os dois últimos são piadas - honestamente!)

Outras dicas

Em MbUnit, você pode usar o atributo RowTest para especificar parâmetros em seu teste.

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

Se você tiver seus três métodos de teste copiados e colados, poderá refatorá-los (método de extração) para se livrar da duplicação.

ou sejaisso é o que eu tinha em mente:

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

Para resumir, existem três caminhos a seguir:

1) Faça os testes em linhas únicas que remetam a métodos comuns (resposta de Rick, também Hallgrim)

2) Use o recurso RowTest do MBUnit para automatizar isso (resposta de Jon Limjap).Eu também usaria um enum aqui, por ex.

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

3) Use uma classe base, resposta de belugabob
Eu fiz uma amostra baseada nesta ideia

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

Isso produz quatro testes de aprovação em duas classes de teste.
As vantagens de 3 são:
1) Menos código extra, menos manutenção
2) Menos digitação para conectar um novo repositório, se necessário - isso seria feito em um só lugar, ao contrário dos outros.

As desvantagens são:
1) Menos flexibilidade para não executar um teste em um provedor, se necessário
2) Mais difícil de ler.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top