Jon-Eric
Jon-Eric

Reputation: 17295

How to structure nose unit tests which build on each other?

Example

Let's say you have a hypothetical API like this:

import foo

account_name = foo.register()
session = foo.login(account_name)
session.do_something()

The key point being that in order to do_something(), you need to be registered and logged in.

Here's an over-simplified, first-pass, suite of unit tests one might write:

# test_foo.py
import foo

def test_registration_should_succeed():
    foo.register()

def test_login_should_succeed():
    account_name = foo.register()
    foo.login(account_name)

def test_do_something_should_succeed():
    account_name = foo.register()
    session = foo.login(account_name)
    session.do_something()

The Problem

When registration fails, all the tests fail and that makes it non-obvious where the real problem is. It looks like everything's broken, but really only one, crucial, thing is broken. It's hard to find that once crucial thing unless you are familiar with all the tests.

The Question

How do you structure your unit tests so that subsequent tests aren't executed when core functionality on which they depend fails?

Ideas

Here are possible solutions I've thought of.

  1. Manually detect failures in each test and and raise SkipTest. - Works, but a lot of manual, error-prone work.
  2. Leverage generators to not yield subsequent tests when the primary ones fail. - Not sure if this would actually work (because how do I "know" the previously yielded test failed).
  3. Group tests into test classes. E.g., these are all the unit tests that require you to be logged in. - Not sure this actually addresses my issue. Wouldn't there be just as many failures?

Upvotes: 2

Views: 303

Answers (2)

Morten
Morten

Reputation: 3854

This problem indicates that Foo is doing to much. You need to separate concerns. Then testing will become easy. If you had a Registrator, a LoginHandler and a DoSomething class, plus a central controller class that orchestrated the workflow, then everything could be tested separately.

Upvotes: 0

Ben Hocking
Ben Hocking

Reputation: 8092

Rather than answering the explicit question, I think a better answer is to use mock objects. In general, unit tests should not require accessing external databases (as is presumably required to log in). However, if you want to have some integration tests that do this (which is a good idea), then those tests should test the integration aspects, and your unit tests should tests the unit aspects. I would go so far as to keep the integration tests and the unit tests in separate files, so that you can quickly run the unit tests on a very regular basis, and run the integration tests on a slightly less regular basis (although still at least once a day).

Upvotes: 4

Related Questions