Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laying out a unit test -- nested class or completely isolated?

Tags:

unit-testing

I've got a class that I've abstracted out, got testable and started to run into an interesting dilemma. Should these test classes be nested? For example ...

public class ValidateCompanyTests
{
     public virtual Setup()
     {
          //setup stuff
     }

     [TestClass]
     public class AndCompanyDoesNotExist : ValidateCompanyTests
     {
          public override Setup()
          {
               base.Setup();
               //setup specific condition
          }

          [TestMethod]
          public void ShouldReturnFalse()
          {

          }

          [TestMethod]
          public void ShouldCreateError()
          {

          }

          [TestMethod]
          public void ShouldSaveError()
          {

          }
     }
}

And almost the same setup for AndCompanyDoesExist, but with more conditions.

My question is, out side of the obvious methods only being printed out in MSTest, I'm not sure if this is a "more correct" way of doing it or I should have each test completely isolated. There'd be a lot of duplicate code (setups, configurations) so that's what lead me to this decision but I wanted some community input, ideas, thoughts.

So lay it on me! (also should this be a community wiki??)

like image 753
jeriley Avatar asked Nov 02 '11 13:11

jeriley


3 Answers

The problem I've had with this approach in the past is that it is deceptively simple... at first. Nesting contexts is great, until you get to your second level of inheritence. Suddenly, you lose track of the world your tests are living in unless you dive through 3 levels of hierarchy, and take lots of notes, draw maps, cast runes, etc.

I favor an approach in which the mechanism to set up the context is explicitly directed by the test itself. Each test class represents a single context, which it sets up either on its own, or by leveraging an external set of helpers to create the needed objects. This way, I'm always able to look at the test initialization method and see at least a high-level description of what "the world" looks like to the tests in that class.

Let the "helpers" leverage each other as much as possible, but the test setup itself should resemble something like this pseudocode: Given_a_valid_Customer() .WithValidOrders(2) .WithInvalidOrders(1);

Given_a_valid_Customer should set up the customer and any required child objects like Addresses. .WithValidOrders(2) adds two valid orders, including whatever child objects are required, like LineItems. Finally, .WithInvalidOrders adds one more order with some fatal flaw.

The point is that the setup isn't bogged down in the details. What makes an order valid or invalid? Who cares, that's not what this test is about. This test only cares that there IS an invalid order amongst several valid ones, and that's the only detail that matters here. The definition of Invalid Order can be defined elsewhere, and then leveraged by multiple test contexts. If something about that definition changes later on, then it can be "trued up" in that one helper method, instead of in hundreds of individual tests.

When I look at this test, I know what its world looks like at exactly the level of detail I care about.

like image 160
Mel Avatar answered Jan 03 '23 13:01

Mel


My personal preference; I would break out the shared setup stuff into a utility class, and keep the actual tests completely separated.

Lots of discussion on this topic here; Unit Testing: Self-contained tests vs code duplication (DRY)

like image 21
XeroxDucati Avatar answered Jan 03 '23 14:01

XeroxDucati


If your goal is to minimize the amount of duplicated setup code I would use inheritance over internal classes. Inhertiance will also give you more flexability for reuse and will make refactoring your tests down the road easier.

like image 26
James Bender Avatar answered Jan 03 '23 14:01

James Bender