【问题标题】:How to run ClassCleanup (MSTest) after each class with test?如何在每节课后运行 ClassCleanup (MSTest) 并进行测试?
【发布时间】:2014-06-09 09:17:26
【问题描述】:

我有几个带有测试套件的课程。

每个测试类都从 ClassInitialize 开始,到 ClassCleanup 结束。 我的问题是 ClassCleanup 不是在每节课结束时调用的,它只在三个类的所有测试之后调用。 我可以解决这个问题吗?谢谢!

[ClassInitialize]
public static void SetUpBrowser(TestContext context)
{
    pageObjectBase.SetBrowser("chrome");
    pagesManager.GetPageObjectBase();
}

[TestMethod]
public void FindCriticalBug()
{
    bla-bla-bla();
}

[ClassCleanup]
public static void CloseBrowser()
{
    pageObjectBase.Stop();
    pagesManager.GeneralClearing();
}

【问题讨论】:

    标签: c# .net mstest


    【解决方案1】:

    测试是无序运行的,包括跨类的测试。请参阅此博客文章:

    https://docs.microsoft.com/archive/blogs/ploeh/classcleanup-may-run-later-than-you-think

    引用:

    无论如何,这是我的输出窗口的结果:

    AssemblyInitialize
    TestClass1: ClassInitialize
    TestClass1: TestInitialize
    TestClass1: MyTestCase1
    TestClass1: TestCleanup
    TestClass2: ClassInitialize
    TestClass2: TestInitialize
    TestClass2: MyTestCase2
    TestClass2: TestCleanup
    TestClass1: ClassCleanup
    TestClass2: ClassCleanup
    AssemblyCleanup
    

    ...这并不意味着 TestClass1 的 ClassCleanup 在类中的最后一个测试用例之后立即执行!实际上,它一直等到所有测试用例都执行完毕,然后执行 与 TestClass2 的 ClassCleanup 一起使用。

    起初这让我很惊讶,但这显然只是因为我 没有真正考虑过:因为原则上测试是, 无序,不能保证 TestClass1 中的所有测试都是 立即连续执行。理论上,执行引擎 可以从 TestClass1 中选择一个测试用例,然后从 TestClass2 中选择一个,然后 另一个来自 TestClass1 等。既然是这样,就没有 保证一个测试类的所有测试之前都已执行 一个新的测试类被初始化,因此,所有的 ClassCleanup 方法 也可以推迟到所有测试用例都执行完毕。

    不幸的是,如果这对您不起作用,您将不得不查看有序测试或不同的单元测试框架。

    【讨论】:

    • 我没有意识到 TestClass1 的 ClassCleanup 直到所有测试用例都被执行后才会执行。我认为这可能就是为什么我只在使用 TestClass1 运行它时才在 TestClass2 中看到错误的原因,但如果我单独运行它则不会。很高兴知道。谢谢。
    【解决方案2】:

    有一个不同的属性称为TestCleanupAttribute,它将在每次测试后运行。

    还有一个属性要在每个测试之前运行,称为TestInitializeAttribute

    这是他们一起运行的一个例子。

    [TestClass]
    public class MyTests
    {
       [ClassInitialize]
       public void ClassInitialize() { Debug.Print("Running ClassInitialize"); }
    
       [TestInitialize]
       public void TestInitialize() { Debug.Print("Running    TestInitialize"); }
    
       [TestMethod]
       public void TestMethod1() { Debug.Print("Running       TestMethod1....."); }
    
       [TestMethod]
       public void TestMethod2() { Debug.Print("Running       TestMethod2....."); }
    
       [TestCleanup]
       public void TestCleanup() { Debug.Print("Running    TestCleanup"); }
    
       [ClassCleanup]
       public void ClassCleanup() { Debug.Print("Running ClassCleanup"); }
    }
    

    这将导致

    Running ClassInitialize
    Running    TestInitialize
    Running       TestMethod1.....
    Running    TestCleanup
    Running    TestInitialize
    Running       TestMethod2.....
    Running    TestCleanup
    Running ClassCleanup
    

    【讨论】:

    • 注意TestMethod1TestMethod2 可能会出现故障。
    • 我不需要使用 TestCleanup。在一个班级的所有测试之后,我需要调用 ClassCleanup。示例:Class1 -> ClassInitialize -> Test1 -> Test2 -> Test3 -> ClassCleanup -> Class2 -> ClassInitialize -> Test4 -> Test5 -> Test6 -> ClassCleanup -> Class3...
    • 看起来框架不支持开箱即用。您必须单独跟踪所有测试,然后在 TestCleanup 中,在它们全部运行后执行检查和清理。
    • Imo,用 [ClassCleanup/Initialize] 修饰的方法应该标记为静态。因为框架实际上为每个测试方法创建了一个新的类实例。静态不会改变实际操作,但它避免有人认为清理和初始化发生在同一个实例中(仅当只有 1 个测试时才成立)。这似乎很奇怪,因为命名与实际发生的情况不符。
    【解决方案3】:

    我做了一些测试,并使用类中的静态字段来“告诉”TestCleanup 方法,所有方法都已运行似乎有效。然后您可以删除 ClassCleanup 并执行以下操作:

    private static int runs = 0;
    
    [ClassInitialize]
    public static void SetUpBrowser(TestContext context)
    {
        pageObjectBase.SetBrowser("chrome");
        pagesManager.GetPageObjectBase();
    }
    
    [TestMethod]
    public void FindCriticalBug()
    {
        runs++;
        bla-bla-bla();
    }
    
    [TestMethod]
    public void FindCriticalBug2()
    {
        runs++;
        ble-ble-ble();
    }
    
    [TestCleanup]
    public static void CloseBrowser()
    {
        if (runs == 2)
        {
            pageObjectBase.Stop();
            pagesManager.GeneralClearing();
        }
    }
    

    我会非常远离这个解决方案,但是如果你没有其他选择,并且你不能重构你的设计来使用提供的生命周期,它可能是一个选择。您可能会更喜欢这里并编写自己的基类来计算执行次数并使用反射获取测试方法的总数来自动化这些东西。

    【讨论】:

    • 您可以通过将runs++ 移动到[TestInitialize] 方法来稍微简化您的代码。然后,如果你在 [TestCleanup] 中减少 runs 并检查它是否是 0,它将支持运行测试的子集。
    • @Grinn 实际上会在第一次测试完成后立即执行干净的逻辑
    • @MarkSegal 这是一个公平的观点。也许我在写这篇文章时正在运行async 测试? ¯(°_o)/¯
    【解决方案4】:

    正常的单元测试是“无序的”,这意味着它们可以按任何顺序运行。您可能正在寻找诸如有序测试之类的东西(请参阅您对多米尼克的评论)。有序测试是一个特殊的单元测试项目。运行有序测试时,测试将按照您的配置方式运行,并在完成后拆除测试类。如果单元测试必须按顺序运行,则测试相互干扰是一种气味。干扰测试是不可靠的,因为它们失败是因为早期的测试留下了一些错误的数据或者测试它自己失败了。你无法知道你的代码到底出了什么问题。

    【讨论】:

    • 感谢您对测试订购的想法!在我的情况下,我只需要在每个带有自动测试的类之后重新启动浏览器,测试顺序没有依赖关系。
    猜你喜欢
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    • 2011-06-05
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    相关资源
    最近更新 更多