使用Nunit TestCaseSource运行测试设置的正确方法

时间:2016-11-09 11:18:41

标签: c# unit-testing nunit testcasesource

我正在尝试使用NUnit中的TestCaseSource运行多个测试。但是当我想要的时候,我很难让[SetUp]运行。

目前它按照我的要求行事,但感觉并不“正确”。所以以下是主要的测试用例代码(简化):

public class ImportTestCases
{

    ImportTestCases()
    {
        TestData.RunTestSetup();
    }

    public static IEnumerable TestCases
    {
        get
        {
            //run the funciton under test...
            var results = RunFunctionSubjectToTest(TestData.ImportantVar);

            //get multiple results...
            var allProperties =new TestCaseData(o).Returns(true)
                ExpandNestedProperties(results.AllProperties)
                    .ToList()
                    .ConvertAll(o => new TestCaseData(o).Returns(true));

            return allProperties;
        }
    }


}


[TestFixture]
public class ImportTests
{

    [TestFixtureSetUp]
    public void ImporTestSetup()
    {
        TestData.RunTestSetup();
    }

    [Test, TestCaseSource(typeof(ImportTestCases), nameof(ImportTestCases.TestCases))]
    public bool PropertyTest(UnitTestHelper.PropInfo info)
    {
        return info.DoTheyMatch;
    }

}

这里的问题是[SetUp]在ImportTestCases“TestCases”之前没有运行 属性“get”运行。 “ImportTestCases”的构造函数也没有运行。因此,为了确保在引用ImportVar之前运行“RunTestSetup”,我必须执行以下操作:

public static class TestData
{
    private static bool HasSetUpRan = false;
    private static int _importantVar;
    public static int ImportantVar
    {
        get
        {
            if(!HasSetUpRan)
            {
                RunTestSetup();
            }
            return _importantVar;
        }
    }        
    public static void RunTestSetup()
    {
        if (HasSetUpRan)
        {
            return;
        }
        ///do set up
        //e.g. _importantVar = GenerateId();
        //end
        HasSetUpRan= true;
    }

}

正如您所看到的,这可确保在返回变量之前已运行Set up。可悲的是,到目前为止,这是我设法让它工作的唯一方法。正如我所说,感觉“错误”而且过于复杂。也许我在这里过度使用testCases?或者我应该使用某种paramatised测试用例(可能吗?)。

我试图简化上面的代码,所以道歉,如果它只是没有意义我想要测试。

主要观点是否在创建TestCaseSources之前运行[Setup]?

4 个答案:

答案 0 :(得分:3)

重点是测试用例将在加载测试时定位。因此,在调用“ TestCases ”属性后,将执行具有[TestFixtureSetUp]属性的例程。但是,您可以在静态构造函数中执行一些设置例程。但是为了首先调用它,你需要将测试数据放在同一个类中:

[TestFixture]
public class ImportTests
{
    static ImportTests() 
    {
        //Step 1 
        //run your set-up routine
    }

    //Step 3
    [Test, TestCaseSource(nameof(ImportTests.TestCases))]
    public bool PropertyTest(string s) => string.IsNullOrEmpty(s);

    //Step 2
    public static IEnumerable TestCases => new[] {new TestCaseData("").Returns(true)};
}

答案 1 :(得分:1)

将测试方法参数设为Func<YOURParamater>。 因此,当Nunit需要获取TestCases列表时,它将返回 Func不是实际参数。 在测试运行时调用此Func并获取您的参数。

Wrap只是一个将lambda返回为Func的函数。 与Func相反,不能将bcs lambda强制转换为对象。

如果您发现此答案不令人满意。请先说明原因,然后再对其进行评分。

public class TestCaseClass
{
    public static IEnumerable Cases
    {
        get
        {

            yield return new TestCaseData(Wrap(() => 1)).SetArgDisplayNames("1");
            yield return new TestCaseData(Wrap(() => 2)).SetArgDisplayNames("2");
        }
    }
    private static Func<T> Wrap<T>(Func<T> fun)
    {
        return fun;
    }
}


[TestCaseSource(typeof(TestCaseClass), nameof(UserTestCases.Cases))]
public bool Tests(Func<int> getInt)
{
    return getInt() == 1;
}

答案 2 :(得分:0)

也许一种可能的解决方案是使您创建TestSource的方法不是静态的并添加默认构造函数。在构造函数中,您可以执行测试用例所需的所有初始化操作。您仍然可以将TestFixtureSetUp用于其他初始化内容。

[TestFixture]
public class ImportTests
{
    public ImportTests()
    {
        //inititalize test case source
    }

    [TestFixtureSetUp]
    public void ImporTestSetup()
    {
        //inititalize rest of test
    }

    public IEnumerable<string> Fields()
    {
        return new[] { "foo", "bar", "foobar" };
    }

    [Test]
    [TestCaseSource("Fields")]
    public void PropertyTest(string info)
    {
       // Assert
    }
}

答案 3 :(得分:0)

你要做的事基本上忽略了NUnit的工作方式。考虑这个顺序:

  1. 您运行GUI并加载测试程序集。
  2. 为了给gui一个测试列表,NUnit执行你的TestCaseSource方法。
  3. 您的测试用例源方法确定将有多少测试以及传递给每个测试用例的参数。
  4. 你坐在办公桌前看着所有测试的名字,或者在推特上张贴。让我们说20分钟。
  5. 您运行所有测试,导致SetUp,TearDown和测试方法本身正在执行。
  6. 你去吃午饭。
  7. 午餐后,您决定再次进行测试。所有相同的SetUp,TestMethod,TearDown内容都会再次执行。只要您没有重新编译重新加载的测试程序集,就不会再次使用测试用例源。
  8. 在决定在测试用例源中做什么时,你必须要注意这个序列,我夸大了它的效果。通常,创建长寿命对象可能不是您想要做的。

    还有一些说明:

    • 在NUnit中,测试用例几乎总是参数化。如果测试没有参数化,那么它不需要一个源,但可以只是一个简单的测试,并且可以进行所有自己的初始化。

    • 标记与测试相同的方法和某人建议的来源是一个非常大的错误 - 病态。因为测试方法和测试用例源方法的目的是完全不同的,所以我们没有把它作为错误捕获的唯一原因是几乎没有人尝试过这样做。

    最好的办法是使用源返回测试可用于实例化所需对象的参数。