我们如何在MSTest中运行具有多个参数的测试方法?

问题描述 投票:0回答:10

NUnit 有一个称为 Values 的功能,如下所示:

[Test]
public void MyTest(
    [Values(1,2,3)] int x,
    [Values("A","B")] string s)
{
    // ...
}

这意味着测试方法将运行六次:

MyTest(1, "A")
MyTest(1, "B")
MyTest(2, "A")
MyTest(2, "B")
MyTest(3, "A")
MyTest(3, "B")

我们现在正在使用 MSTest,但是是否有任何等效项,以便我可以使用多个参数运行相同的测试?

[TestMethod]
public void Mytest()
{
    // ...
}
c# unit-testing nunit mstest vs-unit-testing-framework
10个回答
207
投票

编辑4:看起来这是在2016年6月17日的MSTest V2中完成的:https://blogs.msdn.microsoft.com/visualstudioalm/2016/06/17/take-the-mstest-framework-forward-与-mstest-v2/

原答案

大约一周前,在 Visual Studio 2012 Update 1 中,现在可以实现类似的功能:

[DataTestMethod]
[DataRow(12,3,4)]
[DataRow(12,2,6)]
[DataRow(12,4,3)]
public void DivideTest(int n, int d, int q)
{
  Assert.AreEqual( q, n / d );
}

编辑:看来这仅在 WinRT/Metro 的单元测试项目中可用。真糟糕

编辑 2:以下是在 Visual Studio 中使用“转到定义”找到的元数据:

#region Assembly Microsoft.VisualStudio.TestPlatform.UnitTestFramework.dll, v11.0.0.0
// C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0\ExtensionSDKs\MSTestFramework\11.0\References\CommonConfiguration\neutral\Microsoft.VisualStudio.TestPlatform.UnitTestFramework.dll
#endregion

using System;

namespace Microsoft.VisualStudio.TestPlatform.UnitTestFramework
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class DataTestMethodAttribute : TestMethodAttribute
    {
        public DataTestMethodAttribute();

        public override TestResult[] Execute(ITestMethod testMethod);
    }
}

编辑 3:此问题是在 Visual Studio 的 UserVoice 论坛中提出的。 最后更新状态:

已启动 · Visual Studio 团队管理员 Visual Studio 团队(产品 团队(Microsoft Visual Studio)已回复 · 2016 年 4 月 25 日 谢谢 以获得反馈。我们已经开始致力于此。

Pratap Lakshman Visual Studio

https://visualstudio.uservoice.com/forums/330519-team-services/suggestions/3865310-allow-use-of-datatestmethod-datarow-in-all-unit


71
投票

此功能现已处于“预发布版”状态,可与 Visual Studio 2015 配合使用。 例如:

[TestClass] public class UnitTest1 { [TestMethod] [DataRow(1, 2, 2)] [DataRow(2, 3, 5)] [DataRow(3, 5, 8)] public void AdditionTest(int a, int b, int result) { Assert.AreEqual(result, a + b); } }



49
投票
可扩展性模型,你可以自己实现它

。另一种选择是使用数据驱动测试 我个人的意见是坚持使用 NUnit...

从 Visual Studio 2012 更新 1 开始,MSTest 具有类似的功能。请参阅

McAden 的回答


13
投票
Value

(或

TestCase
)属性不完全相同,但 MSTest 具有
DataSource
 属性,它允许您执行类似的操作。
您可以将其连接到数据库或 XML 文件 - 它不像 NUnit 的功能那么简单,但它可以完成工作。


7
投票
DataSource

。使用它,您可以按照您的要求执行数据驱动的测试。您可以将测试数据保存在 XML、CSV 或数据库中。这里有一些可以指导您的链接

  • 使用 VSTS 2008 进行单元测试(第 3 部分)
  • 如何:创建数据驱动的单元测试
  • 演练:使用配置文件定义数据源

5
投票
TestContext

属性和

TestPropertyAttribute
示例

public TestContext TestContext { get; set; } private List<string> GetProperties() { return TestContext.Properties .Cast<KeyValuePair<string, object>>() .Where(_ => _.Key.StartsWith("par")) .Select(_ => _.Value as string) .ToList(); } //usage [TestMethod] [TestProperty("par1", "http://getbootstrap.com/components/")] [TestProperty("par2", "http://www.wsj.com/europe")] public void SomeTest() { var pars = GetProperties(); //... }

编辑:

我准备了一些扩展方法来简化对

TestContext

属性的访问,并且就像我们有几个测试用例一样。请参阅此处处理简单测试属性的示例:

[TestMethod]
[TestProperty("fileName1", @".\test_file1")]
[TestProperty("fileName2", @".\test_file2")]
[TestProperty("fileName3", @".\test_file3")]
public void TestMethod3()
{
    TestContext.GetMany<string>("fileName").ForEach(fileName =>
    {
        //Arrange
        var f = new FileInfo(fileName);

        //Act
        var isExists = f.Exists;

        //Asssert
        Assert.IsFalse(isExists);
    });
}

以及创建复杂测试对象的示例:

[TestMethod] //Case 1 [TestProperty(nameof(FileDescriptor.FileVersionId), "673C9C2D-A29E-4ACC-90D4-67C52FBA84E4")] //... public void TestMethod2() { //Arrange TestContext.For<FileDescriptor>().Fill(fi => fi.FileVersionId).Fill(fi => fi.Extension).Fill(fi => fi.Name).Fill(fi => fi.CreatedOn, new CultureInfo("en-US", false)).Fill(fi => fi.AccessPolicy) .ForEach(fileInfo => { //Act var fileInfoString = fileInfo.ToString(); //Assert Assert.AreEqual($"Id: {fileInfo.FileVersionId}; Ext: {fileInfo.Extension}; Name: {fileInfo.Name}; Created: {fileInfo.CreatedOn}; AccessPolicy: {fileInfo.AccessPolicy};", fileInfoString); }); }

查看扩展 
methods

和一组 samples 了解更多详细信息。


5
投票
DataRowAttribute

在 Visual Studio 2015 中工作,这就是我最终得到的结果:

[TestClass]
public class Tests
{
    private Foo _toTest;

    [TestInitialize]
    public void Setup()
    {
        this._toTest = new Foo();
    }

    [TestMethod]
    public void ATest()
    {
        this.Perform_ATest(1, 1, 2);
        this.Setup();

        this.Perform_ATest(100, 200, 300);
        this.Setup();

        this.Perform_ATest(817001, 212, 817213);
        this.Setup();
    }

    private void Perform_ATest(int a, int b, int expected)
    {
        // Obviously this would be way more complex...

        Assert.IsTrue(this._toTest.Add(a,b) == expected);
    }
}

public class Foo
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

这里真正的解决方案是仅使用 NUnit(除非您像我在这个特定实例中一样陷入 MSTest)。


4
投票

在 Visual Studio 2012 中,它在 TestExplorer 中创建两个测试:

DemoTest_B10_A5.测试
  1. DemoTest_A12_B4.测试
  2. public class Demo { int a, b; public Demo(int _a, int _b) { this.a = _a; this.b = _b; } public int Sum() { return this.a + this.b; } } public abstract class DemoTestBase { Demo objUnderTest; int expectedSum; public DemoTestBase(int _a, int _b, int _expectedSum) { objUnderTest = new Demo(_a, _b); this.expectedSum = _expectedSum; } [TestMethod] public void test() { Assert.AreEqual(this.expectedSum, this.objUnderTest.Sum()); } } [TestClass] public class DemoTest_A12_B4 : DemoTestBase { public DemoTest_A12_B4() : base(12, 4, 16) { } } public abstract class DemoTest_B10_Base : DemoTestBase { public DemoTest_B10_Base(int _a) : base(_a, 10, _a + 10) { } } [TestClass] public class DemoTest_B10_A5 : DemoTest_B10_Base { public DemoTest_B10_A5() : base(5) { } }

    
        

4
投票

查看

使用 PostSharp 在 MSTest 中启用参数化测试


0
投票

我非常确定代码可以改进,因为我根本没有进行任何优化。欢迎提出改进建议。

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public class ValuesAttribute : Attribute { public object[] Values { get; } public ValuesAttribute(params object[] values) { Values = values; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class ValuesDataSourceAttribute : Attribute, ITestDataSource { public IEnumerable<object[]> GetData(MethodInfo methodInfo) { var values = new List<object[]>(); foreach(var parameter in methodInfo.GetParameters()) { if (parameter.ParameterType == typeof(bool)) values.Add(new object[] { true, false }); else if (parameter.ParameterType.IsEnum) values.Add(Enum.GetValues(parameter.ParameterType).Cast<Object>().ToArray()); else { var attribute = parameter.GetCustomAttribute<ValuesAttribute>() ?? throw new InvalidOperationException($"All parameters must have a {nameof(ValuesAttribute)} attached. ParameterName: {parameter.Name}."); if (attribute.Values?.Any() != true) throw new InvalidOperationException($"All {nameof(ValuesAttribute)} must contain values. ParameterName: {parameter.Name}."); if (attribute.Values.Any(v => !parameter.ParameterType.IsAssignableFrom(v.GetType()))) throw new InvalidOperationException($"All values of {nameof(ValuesAttribute)} must be of type {parameter.ParameterType.Name}. ParameterName: {parameter.Name}."); values.Add(attribute.Values); } } var enumerators = values.Select(v => { var e = v.GetEnumerator(); e.MoveNext(); return e; }).ToArray(); bool doIncrement; do { yield return enumerators.Select(e => e.Current).ToArray(); doIncrement = true; for(var i = enumerators.Length-1; i>=0; i--) { if (doIncrement && !enumerators[i].MoveNext()) { enumerators[i] = values[i].GetEnumerator(); enumerators[i].MoveNext(); } else doIncrement = false; } } while (!doIncrement); //DoIncrement will be true if we completed the first enumerator. The impact of this way is that we create one useless iterator per parameter } }

用途:

[TestMethod] [ValuesDataSource] public void Test([Values("a1", "a2")]string a, [Values(1, 2)] int b, bool c, System.ConsoleModifiers d) { }

© www.soinside.com 2019 - 2024. All rights reserved.