多次运行mstest测试

10

我的一些mstest单元测试帮助检测多线程竞争条件,因此当连续运行多次时它们最有用,但我只想针对特定的测试运行多次,而不是每次都运行。

是否有一种方法可以配置mstest(最好在测试列表编辑器中)来多次运行测试?

6个回答

13

我需要做类似的事情,所以我想出了一个解决方案。

这并不简单,但一旦设置好了一切,您可以在所有项目中重复使用它。 我还在GitHub上提供了这个代码的下载(https://github.com/johnkoerner/MSTestLooper),但以防万一它在某些时候消失了,这里是我是如何做到的。

首先,我们创建一个属性,将其应用于我们的类,告诉它运行所有测试多次。 在一个单独的程序集中进行所有操作,因为DLL需要存放在特殊位置。

[Serializable]
public class TestLooperAttribute :  TestClassExtensionAttribute
{
    private static readonly Uri thisGuy = new Uri("urn:TestLooperAttribute");

    private string _PropertyName;
    public string PropertyName
    {
        get
        { return _PropertyName; }
        set
        {
            _PropertyName = value;
        }
    }
    public override Uri ExtensionId
    {

        get {
            return thisGuy; }
    }


        public override TestExtensionExecution GetExecution()
    {

        return new TestLooperExecution(PropertyName);
    }
}

接下来我们需要创建一个自定义的测试类执行器:

class TestLooperExecution : TestExtensionExecution
{
    private string PropertyName;

    public TestLooperExecution(string PropertyName)
    {
        this.PropertyName = PropertyName;
    }

    public override ITestMethodInvoker CreateTestMethodInvoker(TestMethodInvokerContext InvokerContext)
    {
        return new TestLooperInvoker(InvokerContext, PropertyName);
    }

    public override void Dispose()
    {
        //TODO: Free, release or reset native resources
    }

    public override void Initialize(TestExecution Execution)
    {
        //TODO: Wire up event handlers for test events if needed

    }
}

最后,我们添加一个自定义的调用程序,在那里执行循环:

class TestLooperInvoker : ITestMethodInvoker
{
    private TestMethodInvokerContext m_invokerContext;
    private string PropertyName;

    public TestLooperInvoker(TestMethodInvokerContext InvokerContext, string PropertyName)
    {
        m_invokerContext = InvokerContext;
        this.PropertyName = PropertyName;
    }

    public TestMethodInvokerResult Invoke(params object[] args)
    {

        // Our helper results class to aggregate our test results
        HelperTestResults results = new HelperTestResults();

        IEnumerable<object> objects = m_invokerContext.TestContext.Properties[PropertyName] as IEnumerable<object>;

        foreach (var d in objects)
            results.AddTestResult(m_invokerContext.InnerInvoker.Invoke(d), new object[1] { d.GetType().ToString()});

        var output = results.GetAllResults();
        m_invokerContext.TestContext.WriteLine(output.ExtensionResult.ToString());

        return output;
    }
}

HelperTestResults类仅构建输出字符串,您可以根据需要处理此内容,我不想在此处包含该代码,因为它只会使这篇文章变得更长。

将其编译成DLL,然后您需要将其复制到

C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies

您还需要为该类创建注册表项:

Windows Registry Editor Version 5.00 
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0\EnterpriseTools\QualityTools\TestTypes\{13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b}\TestTypeExtensions\TestLooperAttribute]
"AttributeProvider"="TestLooper.TestLooperAttribute, TestLooper"

现在您已经完成了所有这些工作,您可以最终使用该类:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestLooper;
using System.Collections.Generic;
namespace UnitTestSamples
{
    [TestLooper(PropertyName="strings")]
    public class UnitTest1
    {
        public static List<String> strings = new List<String>();
        private TestContext testContextInstance;

        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }
        [ClassInitialize()]
        public static void Init(TestContext x)
        {
            strings.Add("A");
            strings.Add("B");
            strings.Add("C");
            strings.Add("D");

        }

        [TestInitialize()]
        public void TestInit()
        {
            if (!TestContext.Properties.Contains("strings"))
            testContextInstance.Properties.Add("strings", strings);
        }

        [TestMethod]
        [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "DataDriven1.csv", "DataDriven1#csv", DataAccessMethod.Sequential)]
        [DeploymentItem("DataDriven1.csv")]
        public void TestMethodStrings(string s)

        {
            int value1 = Convert.ToInt32(TestContext.DataRow["Col1"]); ;
            TestContext.WriteLine(String.Format("{0}:{1}", s, value1));
        }
    }
}

请注意,我们的测试方法接受一个参数,该参数来自于测试循环器。我还展示了使用数据驱动测试来演示您可以将两者结合在一起,以在数据集上生成大量排列组合。


我甚至不知道MSTest有这样的可扩展性。这太棒了,谢谢! - Martin Costello

5
[TestMethod()]
public void RepetableTest(){
   for(int i = 0; i < repeatNumber; i++){

     //test code goes here


   }
}

3
请参考此链接 http://stackoverflow.com/questions/25565574/run-unittest-several-time-not-just-loop-it? ,了解为什么仅使用循环无法总是达到所需的结果。 - Ricibob
当您拥有初始化和清理逻辑时,该方法无法正常工作。您可以创建一个特殊的属性来运行您的测试N次,请参见https://dev59.com/OEfRa4cB1Zd3GeqP-p0N#76311034。 - Stanislav Berkov

2
我猜答案是否定的。

不,你可以创建一个特殊的属性来运行你的测试N次,参见 https://dev59.com/OEfRa4cB1Zd3GeqP-p0N#76311034 - Stanislav Berkov

2
考虑创建一个测试来启动几个线程。测试列表不允许您为同一测试创建多个条目。但是,您可以将多线程测试分配到自己的列表中,并仅在想要运行该特定测试时调用它。

这是一个有趣的方法。谢谢。 - Andrew Arnott

0
最简单的方法是使用“运行直到失败”(Ctrl+R,U)。 这个测试将在循环中运行,直到你停止它,同时显示执行测试的状态。在菜单中的样子

0

您可以创建自定义属性,指定数据行重复执行的次数。因此,测试将被执行多次。这个想法来自于https://learn.microsoft.com/en-us/visualstudio/test/how-to-create-a-data-driven-unit-test?view=vs-2022 示例属性类:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace EndToEndTest.Infrastructure;

public class RepeatAttribute : Attribute, ITestDataSource {
private readonly int _count;

public RepeatAttribute(int count = 10) {
    this._count = count;
}

public IEnumerable<object[]> GetData(MethodInfo methodInfo) {
    return Enumerable.Range(1, _count).Select(x => new object[0]);
}

public string GetDisplayName(MethodInfo methodInfo, object[] data) {
    if (data != null) {
        return string.Format(CultureInfo.CurrentCulture, "{0} x{1}", methodInfo.Name, _count);
    }

    return null;
}
}

然后将此属性添加到测试方法中

[TestMethod]
[Repeat(5)]
public void TestAccountSummaryInitialData() {...}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接