Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Taking the following simple method, how would you suggest I write a unit test for it (I am using MSTest however concepts are similar in other tools).

public void MyMethod(MyObject myObj, bool validInput)
{    
 if(!validInput)
 {
  // Do nothing
 }
 else
 {
  // Update the object
  myObj.CurrentDateTime = DateTime.Now;
  myObj.Name = "Hello World";
 }
}

If I try and follow the rule of one assert per test, my logic would be that I should have a Class Initialise method which executes the method and then individual tests which check each property on myobj.

public class void MyTest
{
    MyObj myObj;

    [TestInitialize]
    public void MyTestInitialize()
    {
        this.myObj = new MyObj();
        MyMethod(myObj, true);
    }

    [TestMethod]
    public void IsValidName()
    {
        Assert.AreEqual("Hello World", this.myObj.Name);
    }

    [TestMethod]
    public void IsDateNotNull()
    {
        Assert.IsNotNull(this.myObj.CurrentDateTime);
    }
}

Where I am confused is around the TestInitialize. If I execute the method under TestInitialize, I would need seperate classes per variation of parameter inputs. Is this correct? This would leave me with a huge number of files in my project (unless I have multiple classes per file).

Thanks

share|improve this question
add comment

2 Answers

up vote 7 down vote accepted

You need to look at the "Arrange Act Assert" pattern.

For each test you:

Arrange the Code Under Test and any dependent variables.
Act by calling the method on the Code Under Test.
Assert what you need to ensure the test passes (this should be one thing per test).

In this case you will use:

[TestMethod]
public void MyMethod_CalledWithValidName_HasHelloWorldName()
{
    //Arrange
    bool isValid = true;
    MyObj objectParameter = new MyObj();
    ClassUnderTest objectUnderTest = new ClassUnderTest();

    //Act
    objectUnderTest.MyMethod(objectParameter, isValid);

    //Assert
    string expectedName = "Hello World";
    string actualName = this.myObj.Name;
    Assert.AreEqual(expectedName, actualName);
}

This allows you to see exactly what you are testing and by copying this test and changing the variables, quickly write additional tests for other conditions.

Notice the MethodUnderTest_Condition_ExpectedResult naming pattern for the test.


To actually answer your question:

If I execute the method under TestInitialize, I would need seperate classes per variation of parameter inputs.

Why? TestInitialize is to set up the environment needed for every test in that class and run before each test. In my experience these tend to be relatively small since different methods you are testing have different dependencies and should be isolated enough that you won't need to set up too much the same for each test. If you need to vary the parameter inputs for each test, then it doesn't belong in TestInitialize, it belongs in the Arrange part of your test.

In this class you have a test against a method on an unspecified class (MyMethod), and a test against myObj. These tests should be in different classes.

You are confusing TestInitalize (setup before each test) as ClassInitialize (setup before all tests in that class).

ClassInitialize can be abused and can make the order of the tests important. A test should not depend on another test. You can end up with passes/fails based on the order of test execution, which in some cases can't be specified, and cause breaks when tests are updated or removed or run in isolation.

The way to make sure that myObj.CurrentDateTime isn't null when testing your unspecified class is to set it, you won't be asserting anything on that myObj in those tests. You can assert against them in the test class for myObj. One test class per object with one assert per test.

It also appears that MyMethod is static. This breaks test isolation by meaning that when the code under test runs with works with other production code. This turns this test into an integration test (because the code under test and the static class are both being run during the test); this is bad because a failure in the static class can cause failures in the code under test in methods which are perfectly functional.

I've covered a lot of different aspects of testing in this answer based on a lot of different principles. Grab the fantastic The Art Of Unit Testing and read it cover to cover. It's a great, easy read and is one of the books I'd grab (along with Code Complete) if the office caught fire.

share|improve this answer
    
Similar to the comment below. Using AAA, how would you test that myObj.CurrentDateTime isn't null? Would you create a new test method just for this? –  user460667 Apr 4 '12 at 10:35
    
You wouldn't need to. In this class you have a test against a method on an unspecified class (MyMethod), and a test against myObj. These tests should be in different classes. –  StuperUser Apr 4 '12 at 10:42
    
You are also using TestInitalize as ClassInitialize and making the order of the tests important. A test should not depend on another test. The way to make sure that myObj.CurrentDateTime isn't null for a your unspecified class is to set it, you won't be asserting anything on that myObj in those tests. You can assert against them in the test class for myObj. Still one assert per test. –  StuperUser Apr 4 '12 at 10:45
    
@user460667 I've updated a lot of useful information in this answer. –  StuperUser Apr 4 '12 at 10:53
add comment

you should call the method you are going to test in TestMethod. the TestInitialize function sets everything up to avoid code duplication: it is called each time before a TestMethod function so you start from a blank slate

public class void MyTest {
     MyObj myObj;
      [TestInitialize]
     public void MyTestInitialize()
     {
         this.myObj = new MyObj();

      }

     [TestMethod]
     public void IsValidName()
     {
         MyMethod(myObj, true);
         Assert.AreEqual("Hello World", this.myObj.Name);
     }

     [TestMethod]
     public void IsInvalidName()
     {
         MyMethod(myObj, false);
         Assert.AreNotEqual("Hello World", this.myObj.Name);
     }



} 
share|improve this answer
    
OK, but would you also have an assert in both of those methods to test something like Assert.IsNotNull(myObj.CurrentDateTime)? This I would break the 'single assert per test' rule I have updated my example. –  user460667 Apr 4 '12 at 10:25
    
A good point, worth bearing in mind that while TestInitialize runs once per test, ClassInitialize is run once per test run (of the TestClass), this means that the myObj used in each test is the same. This means that pass/fails of the second test isn't isolated from the behaviour of the first test. For something this simple it doesn't matter, but can cause massive issues if tests earlier in a run initialise a state or mutate the object in a way that later tests depend on. –  StuperUser Apr 4 '12 at 10:28
    
@user460667 you can add a string argument to the assert and display which test failed (or look at the stacktrace if there is any) –  ratchet freak Apr 4 '12 at 10:36
    
+1 Each method is a separate test; the class contains a suite of tests. You should have at least as many asserts as you have tests in the test suite. Note that the one assert per test idea isn't hard and fast -- the idea is just to keep each test focussed on one thing. –  Caleb Apr 4 '12 at 11:28
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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