149

I find the TestCase feature in NUnit quite useful as a quick way to specify test parameters without needing a separate method for each test. Is there anything similar in MSTest?

 [TestFixture]  
 public class StringFormatUtilsTest  
 {  
     [TestCase("tttt", "")]  
     [TestCase("", "")]  
     [TestCase("t3a4b5", "345")]  
     [TestCase("3&5*", "35")]  
     [TestCase("123", "123")]  
     public void StripNonNumeric(string before, string expected)  
     {  
         string actual = FormatUtils.StripNonNumeric(before);  
         Assert.AreEqual(expected, actual);  
     }  
 }  
ardila
  • 1,277
  • 1
  • 13
  • 24
tjjjohnson
  • 3,270
  • 4
  • 31
  • 33
  • Similar question see http://stackoverflow.com/questions/347535/how-to-rowtest-with-mstest/12755816#12755816 – Michael Freidgeim Oct 06 '12 at 01:48
  • Why don't you use NUnit? – Mike de Klerk Sep 01 '15 at 12:39
  • 1
    @MikedeKlerk: Nunit is incredibly slow if you're using Entity Framework... It apparently reflects over all of your generated classes every time you run a test. – Robert Harvey Sep 30 '15 at 15:47
  • Generally speaking, NUnit has more features than MSTest, if you are using MSTest just to be able to run your tests with the VS Test Explorer, then you can do the same with NUnit by installing the NUnit Test Adapter extension via NuGet – Stacked Jan 20 '18 at 11:48
  • 1
    Try ```DataRow()```, see: https://blogs.msdn.microsoft.com/devops/2016/06/17/taking-the-mstest-framework-forward-with-mstest-v2/ – Babak Mar 02 '18 at 13:09
  • Attribute comparison between nunit and mstest https://www.lambdatest.com/blog/nunit-vs-xunit-vs-mstest/ – CharithJ Jul 26 '23 at 00:34

6 Answers6

112

Microsoft recently announced "MSTest V2" (see blog-article). This allows you to consistently (desktop, UWP, ...) use the DataRow-attribute!

 [TestClass]  
 public class StringFormatUtilsTest  
 {  
     [DataTestMethod]  
     [DataRow("tttt", "")]  
     [DataRow("", "")]  
     [DataRow("t3a4b5", "345")]  
     [DataRow("3&5*", "35")]  
     [DataRow("123", "123")]  
     public void StripNonNumeric(string before, string expected)  
     {  
         string actual = FormatUtils.StripNonNumeric(before);  
         Assert.AreEqual(expected, actual);  
     }  
 } 

Again, Visual Studio Express' Test Explorer unfortunately doesn't recognize these tests. But at least the "full" VS versions now support that feature!

To use it, just install the NuGet packages MSTest.TestFramework and MSTest.TestAdapter (both pre-release as of now).

Older answer:

If don't have to stick with MSTest and you're just using it for being able to run the tests via Test Explorer because you only have a Visual Studio Express edition, then this might be a solution for you:

There's the VsTestAdapter VSIX extension for being able to run NUnit tests via Test Explorer. Unfortunately, VS Express users can't install extensions... But fortunately the VsTestAdapter comes with a plain NuGet-Package, too!

So, if you're a VS Express user, just install the VsTestAdapter NuGet-Package and enjoy running your NUnit tests/testcases via Test Explorer!


Unfortunately the aforementioned statement isn't true. While it's perfectly possible to install the package via an Express edition, it's useless, since it can't utilize the Test Explorer. There's previously been a side note on an older version of the TestAdapter, which was removed from the 2.0.0's description page:

Note that it doesn't work with VS Express

Jan Willem B
  • 3,787
  • 1
  • 25
  • 39
Jan Köhler
  • 5,817
  • 5
  • 26
  • 35
  • 4
    MSTest "v2" is now the default framework when using VS 2017. Now that this will be supported OOTB on the latest version, that info should probably go to the top of the answer. – Marc L. Feb 22 '17 at 18:48
  • To get this to work with my old MS Unit Test project I had to delete the testsettings files and downgrade to .NET Framework 4.5 – Matthew Lock Jan 09 '22 at 08:19
37

I know this is a late answer but hopefully it helps others out.

I looked everywhere for an elegant solution and ended up writing one myself. We use it in over 20 projects with thousands of unit tests and hundreds of thousands of iterations. Never once missed a beat.

https://github.com/Thwaitesy/MSTestHacks

1) Install the NuGet package.

2) Inherit your test class from TestBase

public class UnitTest1 : TestBase
{ }

3) Create a Property, Field or Method, that returns IEnumerable

[TestClass]
public class UnitTest1 : TestBase
{
    private IEnumerable<int> Stuff
    {
        get
        {
            //This could do anything, get a dynamic list from anywhere....
            return new List<int> { 1, 2, 3 };
        }
    }
}

4) Add the MSTest DataSource attribute to your test method, pointing back to the IEnumerable name above. This needs to be fully qualified.

[TestMethod]
[DataSource("Namespace.UnitTest1.Stuff")]
public void TestMethod1()
{
    var number = this.TestContext.GetRuntimeDataSourceObject<int>();

    Assert.IsNotNull(number);
}

End Result: 3 iterations just like the normal DataSource :)

using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSTestHacks;

namespace Namespace
{
    [TestClass]
    public class UnitTest1 : TestBase
    {
        private IEnumerable<int> Stuff
        {
            get
            {
                //This could do anything, get a dynamic list from anywhere....
                return new List<int> { 1, 2, 3 };
            }
        }

        [TestMethod]
        [DataSource("Namespace.UnitTest1.Stuff")]
        public void TestMethod1()
        {
            var number = this.TestContext.GetRuntimeDataSourceObject<int>();

            Assert.IsNotNull(number);
        }
    }
}
Thwaitesy
  • 1,503
  • 2
  • 13
  • 15
  • Would this also work with multiple parameters for each case? – Lonefish Aug 11 '16 at 07:17
  • Because there was a change in the underlying library, this does not work with [MsTest "v2"](https://blogs.msdn.microsoft.com/visualstudioalm/2016/06/17/taking-the-mstest-framework-forward-with-mstest-v2/). This is the default for VS15 (VS 2017). – Marc L. Feb 22 '17 at 18:24
  • 4
    Yes, if you are using MSTest V2, there is a new test case provider that is similar to NUnit's. Thus no need for this work around – Thwaitesy Feb 23 '17 at 21:49
  • This doesn't seem to work in VS 2015, the app config file doesn't dynamically populate and thus the datasources aren't found – Reed Nov 09 '17 at 21:37
15

I know this is another late answer, but on my team that is locked into using the MS Test framework, we developed a technique that relies only on Anonymous Types to hold an array of test data, and LINQ to loop through and test each row. It requires no additional classes or frameworks, and tends to be fairly easy to read and understand. It's also much easier to implement than the data-driven tests using external files or a connected database.

For example, say you have an extension method like this:

public static class Extensions
{
    /// <summary>
    /// Get the Qtr with optional offset to add or subtract quarters
    /// </summary>
    public static int GetQuarterNumber(this DateTime parmDate, int offset = 0)
    {
        return (int)Math.Ceiling(parmDate.AddMonths(offset * 3).Month / 3m);
    }
}

You could use and array of Anonymous Types combined to LINQ to write a tests like this:

[TestMethod]
public void MonthReturnsProperQuarterWithOffset()
{
    // Arrange
    var values = new[] {
        new { inputDate = new DateTime(2013, 1, 1), offset = 1, expectedQuarter = 2},
        new { inputDate = new DateTime(2013, 1, 1), offset = -1, expectedQuarter = 4},
        new { inputDate = new DateTime(2013, 4, 1), offset = 1, expectedQuarter = 3},
        new { inputDate = new DateTime(2013, 4, 1), offset = -1, expectedQuarter = 1},
        new { inputDate = new DateTime(2013, 7, 1), offset = 1, expectedQuarter = 4},
        new { inputDate = new DateTime(2013, 7, 1), offset = -1, expectedQuarter = 2},
        new { inputDate = new DateTime(2013, 10, 1), offset = 1, expectedQuarter = 1},
        new { inputDate = new DateTime(2013, 10, 1), offset = -1, expectedQuarter = 3}
        // Could add as many rows as you want, or extract to a private method that
        // builds the array of data
    }; 
    values.ToList().ForEach(val => 
    { 
        // Act 
        int actualQuarter = val.inputDate.GetQuarterNumber(val.offset); 
        // Assert 
        Assert.AreEqual(val.expectedQuarter, actualQuarter, 
            "Failed for inputDate={0}, offset={1} and expectedQuarter={2}.", val.inputDate, val.offset, val.expectedQuarter); 
        }); 
    }
}

When using this technique it's helpful to use a formatted message that includes the input data in the Assert to help you identify which row causes the test to fail.

I've blogged about this solution with more background and detail at AgileCoder.net.

Gary.Ray
  • 6,441
  • 1
  • 27
  • 42
  • 7
    The biggest issue with this is that if any case fails out of the array of values - the entire test fails and no more cases are tested. – ytoledano Apr 16 '16 at 14:33
  • This creates test cases that may influence each other. – BartoszKP Aug 13 '16 at 23:03
  • 1
    @BartoszKP Only if the system under test has side effects, in which case this is probably not an optimal technique. – Gary.Ray Aug 15 '16 at 15:17
  • 1
    @Gary.Ray Also when the system doesn't have side effects _yet_. Also when a test doesn't pass and another developer tries to fix that, and wastes hours trying to figure out whether the dependency matters or not. This is just something you shouldn't ever do, period. – BartoszKP Aug 15 '16 at 20:20
8

Khlr gave a good detailed explanations and apparently this approach started working in VS2015 Express for Desktop. I tried to leave the comment, but my lack of reputation didn't allow me to do so.

Let me copy the solution here:

[TestClass]  
 public class StringFormatUtilsTest  
 {  
     [TestMethod]  
     [DataRow("tttt", "")]  
     [DataRow("", "")]  
     [DataRow("t3a4b5", "345")]  
     [DataRow("3&amp;amp;5*", "35")]  
     [DataRow("123", "123")]  
     public void StripNonNumeric(string before, string expected)  
     {  
         string actual = FormatUtils.StripNonNumeric(before);  
         Assert.AreEqual(expected, actual);  
     }  
 } 

To use it, just install the NuGet packages MSTest.TestFramework and MSTest.TestAdapter.

One problem is

Error CS0433 The type 'TestClassAttribute' exists in both 'Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0 and 'Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0

So, please remove Microsoft.VisualStudio.QualityTools.UnitTestFramework from references of the project.

You're very welcome to edit the original reply and delete this one.

Artem Bobritsky
  • 152
  • 1
  • 9
3

Consider using DynamicDataAttribute:

NUnit Test cases

private static readonly IEnumerable<TestCaseData> _testCases = new[]
{
    new TestCaseData("input value 1").Returns(new NameValueCollection { { "a", "b" } }),
    new TestCaseData("input value 2").Returns(new NameValueCollection { { "a", "b" } }),
    /* .. */
};

[TestCaseSource(nameof(_testCases))]
public NameValueCollection test_test(string str)
{
    var collection = new NameValueCollection();
    collection.TestedMethod(str);
    return collection;
}

MSTest Test cases

private static IEnumerable<object[]> _testCases
{
    get
    {
        return new[]
        {
            new object[] { "input value 1", new NameValueCollection { { "a", "b" } } },
            new object[] { "input value 2", new NameValueCollection { { "a", "b" } } },
            /* .. */
        };
    }
}

[TestMethod]
[DynamicData(nameof(_testCases))]
public void test_test(string str, NameValueCollection expectedResult)
{
    var collection = new NameValueCollection();
    collection.TestedMethod(str);

    CollectionAssert.AreEqual(expectedResult, collection);
}
vladimir
  • 13,428
  • 2
  • 44
  • 70
0

MSTest has the DataSource attribute, which will allow you to feed it a database table, csv, xml, etc. I've used it and it works well. I don't know of a way to put the data right above as attributes as in your question, but it's very easy to set up the external data sources and files can be included in the project. I had it running an hour from when I started, and I'm not an automated test expert.

https://msdn.microsoft.com/en-us/library/ms182527.aspx?f=255&MSPPError=-2147217396 has a full tutorial based on database input.

http://www.rhyous.com/2015/05/11/row-tests-or-paramerterized-tests-mstest-xml/ has a tutorial based on XML file input.

CindyH
  • 2,986
  • 2
  • 24
  • 38