2
\$\begingroup\$

I have some C# unit tests which each basically perform the same steps but supply different arguments to the unit under test.

I wanted to encapsulate the logic inside some "helper methods" but found that the method names, while descriptive, were cumbersome. For example, the code inside a test method would look like this contrived example:

var expected = 1;
var set = new [] { 1, 2, 3, 4, 5 };
AssertThatCallToGetSmallestReturnsExpectedValueFromGivenSet(expected, set);

I simplified the method names and took up a convention of using C#'s named arguments feature:

AssertThatCallToGetSmallest(
    returnsValue: 1, 
    fromSet: new [] { 1, 2, 3, 4, 5 }
);

The latter approach seems cleaner and more readable to me. I've been working with objective-c for a few months now so invoking methods in this manner has become less of an issue for me aesthetically. The other advantage is that this convention scales better with an increase in the number of parameters (if that should ever really be of value).

One thing I'm particularly concerned with is the lack of enforced readability because named arguments are optional. There is the potential for programmers to later come along and "misuse" the helper methods I've created by failing to specify the parameter names:

AssertThatCallToGetSmallest(1, new [] { 1, 2, 3, 4, 5 }); // what does this even mean?

So, which way is "better"?

I'm using the Visual Studio Unit Testing Framework with MSTest.

\$\endgroup\$
  • \$\begingroup\$ Which unit testing framework are you using? Many frameworks allow you to use multiple test cases on a single test method (e.g., NUnit's TestCaseAttribute), but the mechanism to do so will depend on which one you are using. \$\endgroup\$ – Dan Lyons Oct 7 '13 at 18:01
  • \$\begingroup\$ Hey @DanLyons Thanks for the reply. I've updated the post to answer your question. \$\endgroup\$ – MagneticMoose Oct 8 '13 at 0:30
2
\$\begingroup\$

The most canonical way of accomplishing what you want is to use the testing framework's functionality for writing data-driven tests, rather than writing a common helper method and then a number of separate tests which pass arguments into it.

Different frameworks do this differently, so I will focus on MSTest first, as that is the framework you use.

In MSTest, there is a DataSource attribute you can supply, which defines how the framework can locate your test data. Additionally, you supply a TestContext property of type TestContext.

When the tests execute, TestContext will be populated with the data for a single test case, and you can access it by using named indexing into the TestContext.DataRow property.

(Note: I haven't done MSTest in some time, so feel free chime in if there's a better way nowadays)

Example: (Embedded resource: foo.xml)

<?xml version="1.0" encoding="utf-8" ?>
<Tests>
   <Test>
      <a>1</a>
      <b>2</b>
      <result>3<result>
   </Test>
   <Test>
      <a>2</a>
      <b>2</b>
      <result>4<result>
   </Test>
</Tests>

(test file)

  public TestContext TestContext { get; set; }

  [TestMethod]
  [DeploymentItem ("foo.xml")]
  [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.XML",
               "|DataDirectory|\\foo.xml",
               "Test",
               DataAccessMethod.Sequential)]
  public void TestAdd ()
  {
     var first = (int) TestContext.DataRow ["a"];
     var second = (int) TestContext.DataRow ["b"];
     var expected = (int) TestContext.DataRow ["result"];
     var result = testObject.Add(first,second);
     Assert.AreEqual(expected, result);
  }

Adding more test cases is just a matter of adding elements with new data and re-building/re-running.

The How-To MSDN page can be found here, and some examples of DataSource configuration strings can be found here.

Just to give you a taste of this in other frameworks:

NUnit TestCase

  [TestCase(1,2,3)]
  [TestCase(2,2,4)]
  public void TestAdd(int first, int second, int expected)
  {
     var result = testObject.Add(first, second);
     Assert.That(result, Is.EqualTo(expected));
  }

NUnit TestCaseSource

  public static IEnumerable<TestCaseData> TestData
  {
     yield return new TestCaseData(1, 2, 3);
     yield return new TestCaseData(2, 2, 4);
  }

  [TestCaseSource("TestData")]
  public void TestAdd(int first, int second, int expected)
  {
     var result = testObject.Add(first, second);
     Assert.That(result, Is.EqualTo(expected));
  }

MbUnit Row

  [RowTest]
  [Row(1,2,3)]
  [Row(2,2,4)]
  public void TestAdd(int first, int second, int expected)
  {
     var result = testObject.Add(first, second);
     Assert.AreEqual(result, expected);
  }
\$\endgroup\$
  • \$\begingroup\$ I find MSTest's way of doing this comparatively hideous. I was aware of NUnit's TestCase attribute but have avoided using NUnit for reasons. In any case, thanks for the response. I appreciate your effort providing examples. =D \$\endgroup\$ – MagneticMoose Oct 11 '13 at 5:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.