views:

106

answers:

6

I'm writing some NUnit tests for database operations. Obviously, if Add() fails, then Get() will fail as well. However, it looks deceiving when both Add() and Get() fail because it looks like there's two problems instead of just one.

Is there a way to specify an 'order' for tests to run in, in that if the first test fails, the following tests are ignored?

In the same line, is there a way to order the unit test classes themselves? For example, I would like to run my tests for basic database operations first before the tests for round-tripping data from the UI.

Note: This is a little different than having tests depend on each other, it's more like ensuring that something works first before running a bunch of tests. It's a waste of time to, for example, run a bunch of database operations if you can't get a connection to the database in the first place.

Edit: It seems that some people are missing the point. I'm not doing this:

[Test]
public void AddTest()
{
    db.Add(someData);
}

[Test]
public void GetTest()
{
    db.Get(someData);
    Assert.That(data was retrieved successfully);
}

Rather, I'm doing this:

[Test]
public void AddTest()
{
    db.Add(someData);
}

[Test]
public void GetTest()
{
    // need some way here to ensure that db.Add() can actually be performed successfully
    db.Add(someData);
    db.Get(somedata);
    Assert.That(data was retrieved successfully);
}

In other words, I want to ensure that the data can be added in the first place before I can test whether it can be retrieved. People are assuming I'm using data from the first test to pass the second test when this is not the case. I'm trying to ensure that one operation is possible before attempting another that depends on it.

As I said already, you need to ensure you can get a connection to the database before running database operations. Or that you can open a file before performing file operations. Or connect to a server before testing API calls. Or...you get the point.

A: 

Create a global variable and return in the test for Get unless Add set it to true (do this in the last line of Add):

public boolean addFailed = false;
public void testAdd () {
    try {
        ... old test code ...
    } catch (Throwable t) { // Catch all errors
        addFailed = true;
        throw t; // Don't forget to rethrow
    }
}
public void testGet () {
    if (addFailed) return;
    ... old test code ...
}
Aaron Digulla
Thanks. It's not very elegant, but it looks like NUnit doesn't support this functionality natively.
Daniel T.
What you want is a hack, so the solution is one, too ;)
Aaron Digulla
I am not a fan of this solution. The testGet method has not passed, it has not run. It should be reported as such.
mlk
@mlk: It's a hack. Still, there is no point in running a test when you already know it'll fail and bury the single cause of the failure under a heap of inherited errors.
Aaron Digulla
@mlk: It would be nice if the test frameworks had a way to say "test was skipped because it would fail anyway". Python does it that way (for Mac/Windows tests on when you run the suite on Linux, for example).
Aaron Digulla
This solution is dangerously coming close to **wrong** because it depends on the order of the run; currently nunit runs according to the test name so this would be fine. But what if in future Nunit changes the run sequence, that `testGet` is run first before `testAdd`? In this case `testAdd` will be run, but `testGet` won't!
Ngu Soon Hui
@Aaron - It does. Assume, see my answer below.
mlk
@Ngu: Okay, I can accept that. I've reversed the logic.
Aaron Digulla
+1  A: 

I don't think that's possible out-of-box.

Anyway, your test class design as you described will make the test code very fragile.

Ngu Soon Hui
I'm not running the `Add()` test to add data into the database first, then running the `Get()` test to get the data. Rather, I want to ensure that `Add()` works first before the `Get()` test, kinda like saying "Ok, can I add data into the database? Good, now add this test data and see if I can retrieve it."
Daniel T.
A: 

MbUnit seems to have a DependsOnAttribute that would allow you to do what you want.

If the other test fixture or test method fails then this test will not run. Moreover, the dependency forces this test to run after those it depends upon.

Don't know anything about NUnit though.

João Angelo
Thanks, that's pretty much exactly what I was looking for. It's still early enough that I can switch to MbUnit if I want to, so I'll look into it.
Daniel T.
+2  A: 

NUnit supports an "Assume.That" syntax for validating setup. Alas I can not find anything on the subject other than the source code. In the NUnit.Framework namespace is a class Assume. To quote the documentation:

/// Provides static methods to express the assumptions
/// that must be met for a test to give a meaningful
/// result. If an assumption is not met, the test
/// should produce an inconclusive result.

So in context:

public void TestGet() {
    MyList sut = new MyList()
    Object expecting = new Object();
    sut.Put(expecting);
    Assume.That(sut.size(), Is(1));
    Assert.That(sut.Get(), Is(expecting));
}
mlk
+2  A: 

Tests should never depend on each other. You just found out why. Tests that depend on each other are fragile by definition. If you need the data in the DB for the test for Get(), put it there in the setup step.

EricSchaefer
Except it's not fragile. You're missing the point. You need to ensure that certain things are available before you can do testing. In my case, I need to ensure that `Add()` works because without it, `Get()` won't have any data to get. It doesn't matter if it's in the setup or not because I'm using `Add()` in my `Get()` test, but I need to ensure that `Add()` actually works before running the test.
Daniel T.
This is what mocking and stubbing is for. In the test for `Get()`, you should be able to provide dummy data for Get() to work with so that the test can be run without `Add()`. This might mean passing in a subclass of `Add()` that doesn't do anything that could cause it to fail. The point is that a unit test should only test the unit in question. There shoud be a way to test Get() without testing Add(). If you get the fix you're asking for, you can't know whether `Get()` works or not if `Add()` is broken. It means you can't get a reliable snapshot of your system.
jcdyer
@Daniel T. You are missing the point. Tests should always be independent of each other. That means using `Add()` in a test for `Get()` is not a good idea. If `Add()`breaks, the tests for `Get()` will break too. Your test is not isolated. Who says you need to use `Add()` to prepare your DB for `Get()`...
EricSchaefer
@EricSchaefer: How else will you get data into the database? Sure, you can mock and stub it all out, but at that point you're no longer testing the database. What I'm asking for is exactly something to solve the situation you described: a way to not run `Get()` if `Add()` fails.
Daniel T.
How about good ol' `INSERT`?
EricSchaefer
+1  A: 

I think the problem is that you're using NUnit to run something other than the sort of Unit Tests that NUnit was made to run.

Essentially, you want AddTest to run before GetTest, and you want NUnit to stop executing tests if AddTest fails.

The problem is that that's antithetical to unit testing - tests are supposed to be completely independent and run in any order.

The standard concept of Unit Testing is that if you have a test around the 'Add' functionality, then you can use the 'Add' functionality in the 'Get' test and not worry about if 'Add' works within the 'Get' test. You know 'Add' works - you have a test for it.

The 'FIRST' principle (http://agileinaflash.blogspot.com/2009/02/first.html) describes how Unit tests should behave. The test you want to write violates both 'I' (Isolated) and 'R' (Repeatable).

If you're concerned about the database connection dropping between your two tests, I would recommend that rather than connect to a real database during the test, your code should use some sort of a data interface, and for the test, you should be using a mock interface. If the point of the test is to exercise the database connection, then you may simply be using the wrong tool for the job - that's not really a Unit test.

Matt Poush
What you said makes a lot of sense, but I'm still having trouble understanding how I can isolate `Get()` from `Add()`. The way I see it, it's like having an empty box. You have to be able to put something inside the box before you can test whether you can take it out.
Daniel T.
They're about as isolated as they can be. If you want to 'Get()', you obviously need to 'Put()' first in that same test (or in setup). The isolation comes from the fact that your two tests are testing totally different things - your first tests 'Put()', and your second tests 'Get()'.If 'Put()' fails, they'll both fail - but that's OK. There's no Unit Testing rule that says if one area of the system breaks, it should break exactly one test - it just needs to break *at least* one test, hopefully one of which specifically tests that area.
Matt Poush
I had a talk with a co-worker and he said that although in theory, it'd be nice to prevent cascade fails, there's rarely a case in practice where a cascade fail actually hinders anything. In other words, if you have 200+ tests and the test to see if data can be retrieved from the database has failed, it'd be nice to ignore the rest of the tests since they will automatically fail as well. But because tests generally run quickly and should be grouped according to the layer you're testing, it should be pretty easy to see trace down the problem.
Daniel T.