2013-03-22 75 views
13

比方说,我有两种搜索算法的实现,它们为相同的输入返回相同的结果。他们都实现相同的接口。针对不同实现的相同单元测试

如何使用单个[TestClass]来测试两个实现,而不是使用最终相同的逻辑创建两个测试文件?

我可以告诉MSUnit使用不同的构造函数参数两次启动其中一个测试吗?
也许我应该(n)以某种方式注入它?

+0

在你的问题中,你询问MSTest,但是在你的标签中你指定了NUnit。你想要哪一个答案? – 2013-03-22 12:42:24

回答

11

使用abstract test class

[TestClass] 
public abstract class SearchTests 
{ 
    private ISearcher _searcherUnderTest; 

    [TestSetup] 
    public void Setup() 
    { 
     _searcherUnderTest = CreateSearcher(); 
    } 

    protected abstract ISearcher CreateSearcher(); 

    [TestMethod] 
    public void Test1(){/*do stuff to _searcherUnderTest*/ } 

    // more tests... 

    [TestClass] 
    public class CoolSearcherTests : SearcherTests 
    { 
     protected override ISearcher CreateSearcher() 
     { 
      return new CoolSearcher(); 
     } 
    } 

    [TestClass] 
    public class LameSearcherTests : SearcherTests 
    { 
     protected override ISearcher CreateSearcher() 
     { 
      return new LameSearcher(); 
     } 
    } 
} 
+1

这是唯一的好方案。 – 2013-04-03 10:20:58

1

我宁愿有两个不同的[TestMethod]在一个[TestClass]每个测试只有一个实现:这种方式失败的测试将总是正确地指出哪个实现出错。

+2

而你的两个[TestMethod]方法可以简单地作为一行方法,它们都调用实际包含测试代码的相同方法。 – Polyfun 2013-03-22 12:02:44

+0

我的搜索实现有〜10种不同的testmethods。你基本上建议将这些方法复制粘贴到同一类中的20个方法(或者在两个不同的测试类中是10个) – Alex 2013-03-22 12:03:02

+0

@ShellShock,好吧,基本上我现在是如何实现它的,但是我正在寻找一些内置或准备好的功能。 – Alex 2013-03-22 12:03:42

2

你已经用NUnit标记了你的问题,但你问了关于MSTest。你在NUnit中使用参数化测试装置可以获得什么。我不熟悉MSTest在那里建议一个等效的方法,并且快速搜索表明MSTest可能没有此功能。

在NUnit中,您可以通过将多个[TestFixture(...)]属性应用于具有不同参数的夹具类来参数化测试夹具。这些参数将传递给灯具构造函数。

由于可传递参数的类型有限制,因此您可能需要在指定算法时传递一个字符串,然后在构造函数中将提供搜索算法的委托或对象分配给成员字段用于测试。

例如:

using System; 
using System.Collections.Generic; 
using NUnit.Framework; 

namespace MyTests 
{ 
    public static class SearchAlgorithms 
    { 
     public static int DefaultSearch(int target, IList<int> data) 
     { 
      return data.IndexOf(target); 
     } 

     public static int BrokenSearch(int target, IList<int> data) 
     { 
      return 789; 
     } 
    } 

    [TestFixture("forward")] 
    [TestFixture("broken")] 
    public class SearchTests 
    { 
     private Func<int, IList<int>, int> searchMethod; 

     public SearchTests(string algorithmName) 
     { 
      if (algorithmName == "forward") 
      { 
       this.searchMethod = SearchAlgorithms.DefaultSearch; 
       return; 
      } 

      if (algorithmName == "broken") 
      { 
       this.searchMethod = SearchAlgorithms.BrokenSearch; 
      } 
     } 

     [Test] 
     public void SearchFindsCorrectIndex() 
     { 
      Assert.AreEqual(
       1, this.searchMethod(2, new List<int> { 1, 2, 3 })); 
     } 

     [Test] 
     public void SearchReturnsMinusOneWhenTargetNotPresent() 
     { 
      Assert.AreEqual(
       -1, this.searchMethod(4, new List<int> { 1, 2, 3 })); 
     } 
    } 
} 
0

我不能说我用这个方法非常高兴,但这里是我落得这样做。然后我去寻找更好的方法并找到了这个问题。这种方法符合标准:1)我正在使用MS Test,2)我只写了一次测试逻辑,3)我可以判断哪个实现失败(并且双击测试将使我到正确的测试类) 。 该方法使用一个基类来包含所有实际的测试逻辑,然后为每个实现(我有3个)的派生类设置基础接口上的特定实现并覆盖基本测试方法。

[TestClass] 
public abstract class SearchTestBase 
{ 
    protected ISearcher Searcher { get; set; } 

    [TestMethod] 
    public virtual void Find_Results_Correct() 
    { 
     // Arrange (code here) 
     // Act (single line here) 
     var actual = Searcher.Results(input); 
     // Assert 
    } 
} 

(different file...) 
[TestClass] 
public class FastSearcherTest : SearcherTestBase 
{ 
    [TestInitialize] 
    public void TestInitialize() 
    { 
     Searcher = new FastSearcher(); 
    } 

    [TestMethod] 
    public override void Find_Results_Correct() 
    { 
     base.Find_Results_Correct(); 
    } 
} 

(different file...) 
[TestClass] 
public class ThoroughSearcherTest : SearcherTestBase 
{ 
    [TestInitialize] 
    public void TestInitialize() 
    { 
     Searcher = new ThoroughSearcher(); 
    } 

    [TestMethod] 
    public override void Find_Results_Correct() 
    { 
     base.Find_Results_Correct(); 
    } 
} 

所以我不喜欢这种做法是,每次我想添加一个测试,我需要去每个测试文件并覆盖新的测试方法。我喜欢的是你的3个要求。如果我需要更改测试,我只在一个地方改变逻辑。 我看到这个解决方案优于两个测试所称的单个方法的优点是,我不必重复设置正确实现的代码。在这个解决方案中,你有一行调用base.TestName()的单行,而不是两行,一行设置Searcher,另一行调用测试。 Visual Studio也使得编写速度更快......我只需键入“覆盖”并获取选项列表。自动完成为我写剩下的部分。

0

说明基于我的测试。

接受的答案(使用抽象类)只要工作,抽象类和具体类在同一个程序集中。

如果您希望在不同程序集中使用抽象类和具体类,那么KarlZ提到的方法不幸似乎是必需的。不知道为什么会这样。在这种情况下,TestExplorer将不会显示TestMethod。

此外,接受的答案使用嵌套在抽象类中的具体类。这似乎不是一个要求。

使用MSTestV2(1.1.17),VS2017进行测试。 以下是使用的示例类。

Assembly 1 
    [TestClass] 
    public abstract class SampleExternal 
    { 
     [TestMethod] 
     public void SampleTest01() 
     { 
      Assert.IsTrue(false, this.GetType().Name); 
     } 
    } 

Assembly 2 
    [TestClass] 
    public abstract class Sample 
    { 
     [TestMethod] 
     public void SampleTest01() 
     { 
      Assert.IsTrue(false, this.GetType().Name); 
     } 

     [TestClass] 
     public class SampleA : Sample 
     { 
     } 
    } 

    [TestClass] 
    public class SampleB : Sample 
    { 
    } 

    [TestClass] 
    public class SampleC : SampleExternal 
    { 
    } 

    [TestClass] 
    public class SampleD : SampleExternal 
    { 
    } 

使用这些时,测试SampleA和SampleB将执行(和设计失败),但SampleC &采样不会。

相关问题