tags:

views:

340

answers:

5

I was wondering whether the object to test should be a field and thus set up during a SetUp method (ie. JUnit, nUnit, MS Test, …).

Consider the following examples (this is C♯ with MsTest, but the idea should be similar for any other language and testing framework):

public class SomeStuff
{
    public string Value { get; private set; }

    public SomeStuff(string value)
    {
        this.Value = value;
    }
}


[TestClass]
public class SomeStuffTestWithSetUp
{
    private string value;
    private SomeStuff someStuff;

    [TestInitialize]
    public void MyTestInitialize()
    {
        this.value = Guid.NewGuid().ToString();
        this.someStuff = new SomeStuff(this.value);
    }

    [TestCleanup]
    public void MyTestCleanup()
    {
        this.someStuff = null;
        this.value = string.Empty;
    }

    [TestMethod]
    public void TestGetValue()
    {
        Assert.AreEqual(this.value, this.someStuff.Value);
    }
}

[TestClass]
public class SomeStuffTestWithoutSetup
{
    [TestMethod]
    public void TestGetValue()
    {
        string value = Guid.NewGuid().ToString();
        SomeStuff someStuff = new SomeStuff(value);
        Assert.AreEqual(value, someStuff.Value);
    }
}

Of course, with just one test method, the first example is much too long, but with more test methods, this could be safe quite some redundant code.

What are the pros and cons of each approach? Are there any “Best Practices”?

+1  A: 

The second approach is much more readable, and much easier to visually trace.

However, the first approach means less repetition.

What I've found is that I tend to use the SetUp to create objects (especially for things with a number of dependencies), and then set the values used in the test itself. From experience, this provides about the right amount of code-reuse versus readability/traceability.

David Kemp
+2  A: 

It's a slippery slope once you start initializing fields & generally setting up the context of your test within the test method itself. This leads to large test methods and really really unmanageable fixtures that don't explain themselves very well.

Instead, you should look at the BDD style naming & test organization. Make one fixture per context, rather than one fixture per system-under-test. Then your [setup] truly does setup the context, and your tests can be simple one-liner asserts.

It's much easier to read when you see a test output that does this:

OrderFulfillmentServiceTests.cs

  • with_an_order_from_a_new_customer

    • it should check their credit from the credit service
    • it should give no discount
  • with valid credit check

    • it should decrement inventory
    • it should ship the goods
  • with a customer in texas or california

    • it should add appropriate sales tax
  • with an order from a gold customer

    • it should NOT check credit
    • it should get expedited shipping added for free

Our tests are now really good documentation for our system. Each "with_an..." is a test fixture, and the items below it are tests. Within those, you setup the context (the state of the world as the class name describes) and then the test does the simple assert that verifies what the method name says it does.

Ben Scheirman
Yeah, switching to that style of testing has really helped with how stuff goes where. Basically, the Unit Tests follow the same rule as the code: i.e. Single Responibility. One test for one scenerio.
David Kemp
A: 

Personally, I use Setup and Teardown methods for two distinct reasons, although I assume that others will have different reasons.

  1. Use Setup and Teardown methods when there is common initiation logic that is used by all tests and a single instance of the object(s) created in the Setup are designed to be reused.
  2. Use Setup and Teardown methods when the time it takes for creating and destroying any object(s) created takes enough time to slow down the unit testing process when repeated in each TestMethod.

To give you an idea of how often I run accross these scenarios, in a project that I am working on now, only two of my test classes (out of about eighty) have an explicit need for Setup and Teardown methods, both times it was to satisfy my second reason due to the 10 second max I have enabled for each test execution.

I also prefer the readability of having the object(s) created and destroyed within the TestMethod, although it is not a breaking or selling point for me.

joseph.ferris
Don't SetUp and TearDown get called before each TestMethod anyway?
David Kemp
My bad. Don't know where I became misinformed on that. Thank you for the clarification!
joseph.ferris
A: 

From talking with Kent Beck about the design of jUnit I know that Test Classes were a way to share setup between Tests, so using the common initialization was the intent. However, along with that, that means splitting tests that require different setup into separate test classes that have revealing names.

Jeffrey Fredrick
A: 

The approach I take is somewhere in the middle - I use TearDown and SetUp to create a test "sandbox" directory (and delete it when done), as well as to initialize some test member variables with some default values that will be used to test the classes. I then set up some "helper methods" - One is generally called InstantiateClass() I use that to call with the default parameters (if any) which I can override as necessary in each explicit test.

[Test]
public void TestSomething()
{
    _myVar = "value";
    InstantiateClass();
    RunTheClass();
    Assert.IsTrue(this, that);
}
silverbugg