6

Consider unit testing a dictionary object. The first unit tests you might write are a few that simply adds items to the dictionary and check exceptions. The next test may be something like testing that the count is accurate, or that the dictionary returns a correct list of keys or values.

However, each of these later cases requires that the dictionary can first reliably add items. If the tests which add items fail, we have no idea whether our later tests fail because of what they're testing is implemented incorrectly, or because the assumption that we can reliably add items is incorrect.

Can I declare a set of unit tests which cause a given unit test to be inconclusive if any of them fail? If not, how should I best work around this? Have I set up my unit tests wrong, that I'm running into this predicament?

dlras2
  • 8,416
  • 7
  • 51
  • 90
  • 1
    I would consider removing the .net and visual-studio tags. This is a great question about unit testing in general, that has nothing whatsoever to do with either .NET or Visual Studio. – Mike Hofer Feb 21 '12 at 22:04
  • @MikeHofer—My thought was that maybe the VS test suite had an attribute to accomplish this requisite checking, or something. – dlras2 Feb 21 '12 at 22:17
  • 3
    @Dan this would work but the best unit tests are stateless. In other words they are independent of one and other. Also you should aim to test behavior rather than single methods in your tests. For example your production code is unlikely to just add items. Therefore using the retrieval to test your addition working is fine. There is no rule to state that you should only call one method per test. Focus on behavior. If you find such tests are too large or complex you have missing objects that need extracting. – Finglas Feb 21 '12 at 22:19
  • 1
    @Finglas—Good point about testing behavior rather than methods. "Add item, check item's existence" is a perfectly good standalone behavior to test. – dlras2 Feb 21 '12 at 22:21

4 Answers4

3

It's not as hard as it might seem. Let's rephrase the question a bit:

If I test my piece of code which requires System.Collections.Generic.List<T>.Add to work, what should I do when one day Microsoft decides to break .Add on List<T>? Do I make my tests depending on this to work inconclusive?

Answer to the above is obvious; you don't. You let them fail for one simple reason - your assumptions have failed, and test should fail. It's the same here. Once you get your add tests to work, from that point on you assume add works. You shouldn't treat your tested code any differently than 3rd party tested code. Once it's proven to work, you assume it indeed does.

On a different note, you can utilize concept called guard assertions. In your remove test, after the arrange phase you introduce additional assert phase, which verifies your initial assumptions (in this case - that the add is working). More information about this technique can be found here.

To add an example, NUnit uses the concept above disguised under the name Theory. This does exactly what you proposed (yet it seems to be more related to data driven testing rather than general utility):

The theory itself is responsible for ensuring that all data supplied meets its assumptions. It does this by use of the Assume.That(...) construct, which works just like Assert.That(...) but does not cause a failure. If the assumption is not satisfied for a particular test case, that case returns an Inconclusive result, rather than a Success or Failure.

However, I think what Mark Seemann states in an answer to the question I linked makes the most sense:

There may be many preconditions that need to be satisfied for a given test case, so you may need more than one Guard Assertion. Instead of repeating those in all tests, having one (and one only) test for each precondition keeps your test code more mantainable, since you will have less repetition that way.

Community
  • 1
  • 1
k.m
  • 30,794
  • 10
  • 62
  • 86
1

Nice question, I often ponder this and had this problem the other day. What I did was get the basics of our collection working using a dictionary behind the scenes. For example:

public class MyCollection
{
    private IDictionary<string, int> backingStore;

    public MyCollection(IDictionary<string, int> backingStore)
    {
         _backingStore = backingStore;
    }
}

Then we test drove the addition implementation. As we had the dictionary by reference we could assert that after adding items our business logic was correct.

For example the pseudo code for the additon was something like:

public void Add(Item item)
{
    // Check we have not added before
    // More business logic...
    // Add
}

Then the test could be written such as:

var subject = new MyCollection(backingStore);
subject.Add(new Item())
Assert.That(backingStore.Contains(itemThatWeAdded)

We then went on to drive out the other methods such as retrieval, and deletion.

Your question is what should you do with regards the addition breaking, in turn breaking the retrieval. This is a catch 22 scenario. Personally I'd rather ditch the backing store and use this as an implementation detail. So this is what we did. We refactored the tests to use the system under test, rather than the backing store for the asserts. The great thing about the backing store being public initially is it allows you test drive small parts of the codebase, rather than having to implement both addition and retrieval in one go.

The test for addition then looked like the following after we refactored the collection to not expose the backing store.

var subject = new MyCollection();
var item = new Item()
subject.Add(item)
Assert.That(subject.Has(item), Is.True);

In this case I think this is fine. If you can not add items successfully then you sure as hell cannot retrieve anything because you've not added them. As long as your tests are named well any developer seeing some test such as "CanOnlyAddUniqueItemsToCollection" will point future developers in the right direction, in other words, the addition is broken. Just make sure your tests are named well and you should be giving as much help as possible.

Finglas
  • 15,518
  • 10
  • 56
  • 89
0

I don't see this as too much of a problem. If your Dictionary class is not too big, and the unit test for that class is the only unit test testing that code, then when your add method is broken and multiple tests fail, you still know the problem is in the Dictionary class and can identify it, debug and fix it easily.

Where it becomes a problem is when you have other code smells or design problems such as:

  • unit tests tests are testing many application classes, using mocks instead can help here.
  • unit tests are actually system tests creating and testing many application classes at once.
  • the Dictionary class is too big and complex so when it breaks and tests fail it's difficult to figure out what part is broken.
Alb
  • 3,601
  • 6
  • 33
  • 38
-1

This is very interesting. We use NUnit and the best I can tell it runs test-methods alphabetically. That might be an overly-artificial way to order your tests, but if you built up your test classes such that alphabetically/numerically-named pre-req methods came first you might accomplish what you want.

I find myself writing a test method, firing just it to watch it fail, and then writing the code to make it pass. When I'm all done I can run the whole class and everything passes - it doesn't matter what order the tests ran in becuase everything 'works' becuase of the incremental dev I did.

Now later on if I break something in the thing i'm testing who knows what all will fail in the harness. I guess it doesn't really matter to me - I've got a long list of failures and I can tease out what went wrong.

n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • 2
    Very bad idea. You cannot rely on this, whats to say that in version .X they don't change this? Also introducing state into unit tests is a big no no. – Finglas Feb 21 '12 at 22:29
  • @Finglas: my point is IT DOESN'T MATTER. *I* run the tests in the order I want them to run as I develop them. – n8wrl Feb 21 '12 at 22:31
  • *You* running the tests in that order is no longer an automated unit test. – dlras2 Feb 21 '12 at 22:40
  • That was directed at your point regarding the order in with NUnit happens to run tests. – Finglas Feb 21 '12 at 22:41
  • 3
    The only guarantee for the order you should be working with is that they are run in a random order each time. – monksy Feb 21 '12 at 22:48
  • Ok I'm sorry everybody. I wasn't clear at all and I appreciate the explanation for the down-vote. What I meant was, I run the tests manually in the order I develop the functionality, and then the tests run in whatever order the tool runs them in, and it doesn't matter becuase if 'x' breaks everything that depends on 'x' breaks too and tests will fail. Sorry for the confusion. – n8wrl Feb 22 '12 at 13:09