11
\$\begingroup\$

I have some simple tests written, however, the names are not good...I think, any suggestions on better names? I'm not really sure how these tests should be named.

I'm looking for a format or pattern I can follow.

[TestClass]
public class DoorTests
{
    [TestMethod]
    public void DoorIsNotVirtualIfNameStartsWithLetterOtherThanV()
    {
        var door = new Door {Name = "R143"};
        Assert.IsFalse(door.IsVirtual);
    }

    [TestMethod]
    public void DoorIsVirtualIfNameStartsWithLetterV()
    {
        var door = new Door {Name = "V001"};
        Assert.IsTrue(door.IsVirtual);
    }
}
\$\endgroup\$
3
\$\begingroup\$

I like the BDD style test structure.

GivenAnObject WhenIPerformAnAction ThenIGetTheseResults

In C# this translates nicely to a namespace structure:

namespace GivenAnObject
{
  [TestFixture]
  class WhenIPerformAnAction
  {
    private MyObject result;

    [SetUp]
    public void GivenAnObject()
    {
      // CreateMyObject();
      When();
    }

    public void When()
    {
      result = MyObject.ExecuteAction();
    }

    [Test]
    public void ThenIGetAResult()
    {
      Assert.AreEqual(ExpectedResult, result);
    }
  }
}

When run in nunit tests that follow this paradigm will display very nicely as a well grouped set of tests which are testing the behaviour of the various parts of your program.

In your case these tests would be:

GivenADoor WhenTheDoorStartsWithV ThenTheDoorIsVirtual

GivenADoor WhenTheDoorDoesNotStartWithV ThenTheDoorIsNotVirtual

\$\endgroup\$
  • \$\begingroup\$ This is actually what I've been doing bite for a while, I find it works very well. \$\endgroup\$ – CaffGeek Mar 12 '14 at 4:41
15
\$\begingroup\$

I find that Roy Osherove's test naming convention is easy to apply and provides suggestive names for the test methods.

Roy proposes that the test method names should be composed of three parts:

  • MethodName (or PropertyName, as in our case)
  • StateUnderTest (a brief description of the scenario being tested)
  • ExpectedBehavior

Following this convention, the methods in the DoorTest class could be named:

  • IsVirtual_NameStartsWithLetterOtherThanV_ReturnsFalse
  • IsVirtual_NameStartsWithLetterV_ReturnsTrue

Following a convention like this can provides consistency and makes it easy to use and maintain the test code.

Also, having the three elements delimited by underscores provides good readability: In case a test fails, with only a glance, you are easily able to grasp all relevant information about the failure: what component failed? what was the failure scenario? what is the expectancy that was not met?

\$\endgroup\$
  • \$\begingroup\$ Thank you for introducing me to a great convention. Would you agree that for cases like this, it is suitable to break general naming conventions for the language the tests are expressed in? Some people get very precious about these things.. \$\endgroup\$ – obfuscation May 10 '12 at 10:52
  • \$\begingroup\$ @obfuscation - of course, it's OK to break or adapt the convention when there's a good enough reason. After all, it's a convention, not a hard rule. \$\endgroup\$ – Cristian Lupascu May 10 '12 at 12:00
  • \$\begingroup\$ I like the names, and, despite requesting the unblocking of Osherove's site last week, it's still blocked...no clue why it's blocked in the first place. Some days ... grrr \$\endgroup\$ – CaffGeek May 10 '12 at 15:32
  • \$\begingroup\$ @Chad Is it blocked because of office policies? That's weird... \$\endgroup\$ – Cristian Lupascu May 11 '12 at 7:01
  • 1
    \$\begingroup\$ @Chad Is this not where proxies, tunneling, mobile internet or home internet come into play :)? \$\endgroup\$ – obfuscation May 14 '12 at 13:31
7
\$\begingroup\$

I like to use something very similar to what is here referred to as Roy Osherove's test naming convention, the difference being the order of the parts:

  • IsVirtual_ReturnsFalse_IfNameStartsWithLetterOtherThanV
  • IsVirtual_ReturnsTrue_IfNameStartsWithLetterV

Also, I tend to use "When" instead of "If" (but that's just a matter of taste, I guess):

  • IsVirtual_ReturnsFalse_WhenNameStartsWithLetterOtherThanV
  • IsVirtual_ReturnsTrue_WhenNameStartsWithLetterV

The main reason for this reordering of the parts is readability of the test names; I think it reads more naturally when in this order. Try comparing these two variations of naming the same test by reading their names out loud:

  • IsVirtual_NameStartsWithLetterOtherThanV_ReturnsFalse
  • IsVirtual_ReturnsFalse_WhenNameStartsWithLetterOtherThanV

Which one would you prefer?

\$\endgroup\$
3
\$\begingroup\$

In my opinion, the names of your tests are fine. The explain in a way so everyone understands what they do and what they test. The problem I find with your tests are the CamelCases.

Even though I don't care if using CamelCase or underscores in production code, I find tests a lot easier to read by have all small letter and using underscores to separate. The reason I feel this is good in tests is that test names are often much longer than production code names, as they are only typed once. The Roy Osherove's test naming convention also seems descriptive though, but don't really see the need for it if you can read the name of your test as a plain English sentence and understand what we are after.

\$\endgroup\$
  • \$\begingroup\$ Plus 1 for this. Instead of DoorIsNotVirtualIfNameStartsWithLetterOtherThanV name the method door_is_not_virtual_if_name_starts_with_letter_other_than_v. This describes the test, is easy to read, and, as an added bonus, visually clearly separates from the CamelCase methods used in production code. \$\endgroup\$ – ZeroOne May 11 '12 at 11:51
3
\$\begingroup\$

I find these test names cumbersome to the point of being confusing; method names are not camelcased sentences.

If you want to go with "readable" test method names - try to make it easier to read the method name than the code (which is what a 12 word method name is likely to make a reader do). Here are some suggestions for DoorIsNotVirtualIfNameStartsWithLetterOtherThanV -

  • The class is testing the class door - all methods in this test case will test something in the door class - the word door is not necessary.

IsNotVirtualIfNameStartsWithLetterOtherThanV

  • Remove words that don't add meaning

notVirtualIfNameStartsLetterOtherThanV

  • Use a simpler vocabulary

notVirtualIfNameFirstCharNotV

  • Double negatives are confusing

notVirtualIfNameFirstCharR

But. What's wrong with

[TestClass]
public class DoorTests
{
    [TestMethod]
    /**
     * Virtual doors only start with V
     */
    public void testIsVirtual()
    {
        var door = new Door {Name = "V001"};
        Assert.IsTrue(door.IsVirtual, "Door $door$ is not virtual but was expected to be");
    }

    [TestMethod]
    /**
     * Real doors don't start with V, and usually start with R
     */
    public void testIsVirtualFalse()
    {
        var door = new Door {Name = "R143"};
        Assert.IsFalse(door.IsVirtual, "Door $door$ isVirtual, but was expected not to be");
    }
}

If you have more permutations of similar tests - use a (short) suffix. Just ensure the message when an assert fails gives all the detail you'd want to know what the failure means, or the docs for the failing test clearly guide the developer to understand the error. Replacing docs and error messages with "meaningful method names" just makes code tiresome to read.

\$\endgroup\$
  • \$\begingroup\$ The names aren't bad, but I really don't like comments to describe anything...they end up being forgotten, and eventually fall out of date and loose accuracy. \$\endgroup\$ – CaffGeek May 10 '12 at 15:33
0
\$\begingroup\$

I like the tests layout, described here: http://zendeveloper.blogspot.com/2012/01/structuring-unit-tests.html

In summary, all tests for a given methods or property are grouped in their own "sub-fixture", and then I use more descriptive names for the test cases. Also, check FluentAssertions, they are very good to describe the "assert" part in more natural language.

[TestFixture]
public class DoorTests
{
   [TestFixture]
   public class IsVirtualProperty
   {
       [Test]
       public void Should_be_false_if_Name_does_not_start_with_letter_V()
       {
          var door = new Door {Name = "R123"};
          door.IsVirtual.Should().Be.False;
       }
       [Test]
       public void Should_be_true_if_Name_start_with_letter_V()
       {
          var door = new Door {Name = "V123"};
          door.IsVirtual.Should().Be.True;
       }
   }
}

As an addition, the first test does not cover all the possibilities, it just verifies that if the name starts with R, IsVirtual is false :)

\$\endgroup\$

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.