3

我一直在按照 Rob Connery 的 Asp.net MVC 店面用 C# 制作一个小玩具 Web 应用程序。

我发现我有一个存储库接口,称之为 IFooRepository,带有方法,比如说

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

我有这三个实现:ISqlFooRepository、IFileFooRepository 和 IMockFooRepository。

我也有一些测试用例。我想做的,但还没有想出如何去做,是对这三个实现中的每一个运行相同的测试用例,并为每个接口类型的每个测试通过一个绿色勾号。

例如

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

我希望这个测试方法运行 3 次,环境中的一些变化允许它获取三种不同类型的存储库。目前我有三个剪切和粘贴的测试类,它们仅在私有帮助器方法 IFooRepository GetRepository() 的实现上有所不同;显然,这很臭。

但是,我不能仅仅通过合并剪切和粘贴方法来消除重复,因为它们需要存在、公开并标记为测试才能运行测试。

我正在使用 Microsoft 测试框架,如果可以的话,我更愿意继续使用它。但是关于如何在 MBUnit 中执行此操作的建议也会引起一些兴趣。

4

5 回答 5

3

在 MbUnit 中,您可能能够使用 RowTest 属性来指定测试的参数。

[RowTest]
[Row(new ThisRepository())]
[Row(new ThatRepository())]
Public void GetFoo_NotNull_Test(IFooRepository repository)
{
   var results = repository.GetFoo();
   Assert.IsNotNull(results);
}
于 2008-09-17T09:07:09.603 回答
3

创建一个包含测试的具体版本的抽象类和一个返回 IFooRepository 的抽象 GetRepository 方法。创建三个派生自抽象类的类,每个类都以返回适当 IFooRepository 实现的方式实现 GetRepository。将所有三个类添加到您的测试套件中,您就可以开始了。

为了能够有选择地为某些提供程序而不是其他提供程序运行测试,请考虑使用 MbUnit '[FixtureCategory]' 属性对您的测试进行分类 - 建议的类别是 'quick' 'slow' 'db' 'important' 和 'unimportant' (最后两个是笑话 - 老实说!)

于 2008-09-17T09:32:03.663 回答
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);
}
于 2008-09-17T09:16:56.400 回答
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);
}
于 2008-09-17T10:26:41.393 回答
0

总结一下,有以下三种方法:

1)使测试成为调用常用方法的衬线(Rick 的回答,也是 Hallgrim)

2)使用 MBUnit 的 RowTest 功能自动执行此操作(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)如果需要,插入新存储库的输入最少 - 与其他地方不同,它将在一个地方完成。

缺点是:
1) 如果需要,不针对提供者运行测试的灵活性较低
2) 更难阅读。

于 2008-09-17T12:22:21.850 回答