Belphegor
Belphegor

Reputation: 1843

Should a unit test include the converse of what you are testing?

Imagine that we have a game with different types of servers in it to represent different countries. Now, lets say the game only allows users to "buddy" other players that are only in the same server that their account is attached to. As a developer, I am tasked to write a test case only to see if the feature for users to "buddy" one another works. However, here is where my dilemma lies, since I only have to test whether or not a user can "buddy" someone else, do I have to also test if the user cannot add users from another server in this same test case, or should that be written in another test case totally separate from the current one that I am writing?

Upvotes: 1

Views: 20

Answers (1)

Schwern
Schwern

Reputation: 164809

Yes, you should.

The first test you're describing, where you just test if they can have a buddy, is known as Happy Path testing. You're testing that it works with no exceptions or errors or weird use cases.

Happy Path testing is a good start. Everything else is where the real fun begins.

In your case, the things that come to mind are...

  • What if they buddy with an invalid buddy?
  • What if they buddy with someone who is already their buddy?
  • What if they buddy with something that's not a user?

How these should be organized is a matter of taste. Ideally they're all separate tests. This makes the purpose of each test clear via the test name, and it avoids interactions and dependencies between the tests. Here's a sketch in no particular language.

describe add_buddy {
    test happy_path {
        assert user.add_buddy(valid_buddy);
        assert user.buddies.contains(valid_buddy);
    }

    test buddy_on_another_server {
        buddies = user.buddies;
        assert !buddies.contains(invalid_buddy);

        assertThrows {
            user.add_buddy(invalid_buddy);
        } InvalidBuddy;

        assert buddies == user.buddies, "buddy list unchanged";
    }

    test buddy_with_non_user {
        buddies = user.buddies;

        assertThrows {
            user.add_buddy(non_user);
        } ArgumentError;

        assert buddies == user.buddies, "buddy list unchanged";
    }

    test buddy_an_existing_buddy {
        assert user.add_buddy(valid_buddy);

        # Should this return true? False? An exception?
        # You have to decide. I decided false.
        assert !user.add_buddy(valid_buddy);

        # The buddy is only on the list once.
        assert user.buddies.numContains(valid_buddy) == 1;
    }
}

Things like user and valid_buddy can be created in the setup routine, or better they're available via a fixtures generator such as Factory_Girl.

Upvotes: 1

Related Questions