Compartilhar via


Refactoring: Introduce SetUp Method

Tests in a TestFixture share common initialization code.

Create a method in the TestFixture, move common initialization code to the method, and execute the method prior to each test.   

Before:

[

Test]
public void UponCreationCountIsZero()
{
BookmarkCollection collection = new BookmarkCollection();
Assert.AreEqual(0, collection.Count);
}

[

Test]
public void AddBookmarkCountIsIncremented()
{
BookmarkCollection collection = new BookmarkCollection();
collection.Add("Label", new Uri(https://example.com));
Assert.AreEqual(1, collection.Count);
}

After the Refactoring:

[

TestFixture]
public class BookmarkCollectionFixture
{
private static BookmarkCollection collection;

[

SetUp]
public void BeforeTest()
{
collection = new BookmarkCollection();
}

[

Test]
public void UponCreationCountIsZero()
{
Assert.AreEqual(0, collection.Count);
}

[

Test]
public void AddBookmarkCountIsIncremented()
{
collection.Add("Label", new Uri(https://example.com));
Assert.AreEqual(1, collection.Count);
}
}

The common code in this case is the creation of the BookmarkCollection in each test method. That code is moved to the [SetUp] method. NUnit insures that the method is executed prior to each test being executed.

This example demonstrates the need to keep the test code as clean as the production code. This means that if you see duplication it is your job to remove it, even in the test code. As a side note, I do agree with Brian Button that doing this refactoring does reduce the communication of the test code. His reasoning is that after the refactoring I have to look at the [SetUp] method and the individual [Test] methods to understand the full extent of the tests. This example illustrates the trade-off between removing duplication and code communication. I am interested in hearing opinions on the trade-off in this example and your everyday experience.

Lastly, there is another refactoring, named Introduce TearDown Method which removes common clean-up code from the tests to a method that is executed after the tests are run.

Comments

  • Anonymous
    November 15, 2004
    The comment has been removed

  • Anonymous
    November 15, 2004
    The comment has been removed

  • Anonymous
    November 15, 2004
    Here's a simple one: why make the collection static?

  • Anonymous
    November 15, 2004
    I agree with Ben, I wouldn't make the collection static until it was needed to make a test pass. This would seem to follow the "simple design" rule of doing the least you can do to make the test pass.

  • Anonymous
    November 15, 2004
    In this simple case it's not hard to see the "intent" of the setup. But I have seen this taken to the extreme where the setup was doing way too much. In complex setup scenarios it's often hard to find/see the intent of a setup method. What's is it setting up? How is it going about doing it? All questions I find myself asking a lot.

    I've also seen setup code doing setup stuff for 5 test methods but the other 30 test methods don't need that piece of the setup. In those cases I advocate having a private method that is called first for the 5 methods that must have that functionality.

  • Anonymous
    November 15, 2004
    Yes it does reduce the communication of the method, but no more so than code-interception style AOP. I think the price to pay is small enough that it doesn't matter, especially if your test fixture is small enough.

    Also, I'm sure later you'll want to add a certain number of items to the collection for various testing reasons. I usually break that functionality out into a separate private method, which, when well-named, doesn't reduce the communication of the test.

  • Anonymous
    November 15, 2004
    The comment has been removed

  • Anonymous
    November 15, 2004
    I recently wrote hundreds of tests to cover our legacy business tier code. I created a TestBase class, and inherited all my tests from this class. In my base class, I had a virtual [SetUp]Init() that could be overridden if need be in my test fixture.

    There was a good deal of setup involved for each test, mostly setting up user principal objects to flow through the tiers, and this worked out really well. This probably obfuscates the intent of the tests to an even greater degree, but I agree with Darrell that it's a small price to pay.

    About small fixtures, I think this should be a best practice, if it's not already. One thing I wish I'd done was reduce the number of tests per fixture. Especially when using TestDriven.NET and "Test w/ Debugger". If I had 20 tests in a fixture, test w/ debugger would test them al. I'd have rather tested fewer if I had to resort to the debugger.

    All in all it was a great experience, I identified lots of dead and broken code, and now have a good baseline for refactoring.

    Thanks James!

  • Anonymous
    November 15, 2004
    Tony,

    I don't think that's entirely true. If you name your method Test(something) then, yes, it will be loaded into NUnit, regardless of the [Test] attribute. But to me this is just a feature of NUnit. Using the attribute seems like it's a much better way to write a test.

  • Anonymous
    November 15, 2004
    Actually, my bad, all my test methods are prefixed with the words "Test", and therefore would not need the TestAttribute.

  • Anonymous
    November 16, 2004
    I was reading the release notes for the next version of NUnit and I see that it will become a configuration option to recognize methods by name (i.e. "Test") as opposed to the attribute.

  • Anonymous
    November 17, 2004
    I did what Brendan mentioned above as well. I took it a step further and have testSetup and testTearDown stored procedures that are invoked from the base UnitTest class' Setup and TearDown methods. testTearDown truncates tables and uses DBCC to reset the identities to 1. testSetup (hard coded right now) does a bunch of inserts to various tables.

    The net effect is that before each test is run the database is in a known, clean, predicatable state. Each project has a different DB schema and such so each testSetup has to be unique and different.

  • Anonymous
    December 07, 2004
    I think that Refactoring at all level should be applied. Eliminating dupblicate code is a goal every programmer should tend to, that for Production or Test code.

    I belive that communication is an issue, but should not stop you from using it. It should be implicit that [SetUp] should be executed before every test. Since it not today common knowledge for everyone, does not mean we should not use it. It will become common knowledge. From that point on, the communication won't be an issue anymore.

    It's like Design Patterns. Will you not use them because not all programmers know them? Of course not. The strategy is to use them and transmit the knowledge to your team.

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=2413

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=20049

  • Anonymous
    June 18, 2009
    PingBack from http://homelightingconcept.info/story.php?id=1589