6

One of the things I struggle with when unit testing is deciding what behaviors to actually test. I think part of my struggle stems from how most unit testing tutorials don't use good examples of things you would actually want to test in real life. For example, the tutorial I'm looking at now has a test basically like this:

[Test]
public void TestQuizIsInitializedCorrectly() {
  var question = QuizQuestion() { 
      Question = "What is your favorite color?",
      Answer = "Blue, no yelloooowwww........" }

  var game = new Game(new List<QuizQuestion>(new[] { question }));
  Assert.AreEqual(1, game.Questions.Count);
}

What is the point of this test? To validate that when you put one object into a List in C#, that the List still has only 1 object? That's silly and I could spend a million years writing unit tests that validate simple things like putting an object into a list and validating the list count is 1. I would never finish my app. Are these just bad examples of what to test so we can focus on how to test in the tutorial?

Or do people really write tests like this? Maybe tests like this are not really testing your own code, but they are formalizing an assumption about how we expect people to use the Game class? Perhaps the above example might be written not so much to test that the question was successfully initialized into the List, but rather to formalizing an assumption that that the Game class can only be passed in questions, it can't add default questions itself. I might know it's not doing that now, but putting it in a unit test makes it so we know right away if some future developer changes the Game class to invalidate that assumption.

Is this a common use case in unit testing? Writing tests not so much to test code that's written but to formalize assumptions to protect against future changes?

4
  • Possible duplicate of What should you test with unit tests?
    – gnat
    Commented Dec 15, 2016 at 13:32
  • 1
    Don't forget that unit tests are just the beginning. You should also write functional, integration and system tests to validate more complex interactions. Each layer of automated testing builds off the previous layer.
    – Adrian
    Commented Dec 15, 2016 at 13:35
  • Part of the problem is the age old dichotomy between a generic example and one that assumes specific domain knowledge.
    – Robbie Dee
    Commented Dec 15, 2016 at 15:37
  • Ideally most assumptions (preconditions, post conditions, and invariants) should be documented and enforced in the code, not in the tests, except for those which are too time consuming to test at run-time. Commented Dec 15, 2016 at 17:56

3 Answers 3

11

To validate that when you put one object into a List in C#, that the List still has only 1 object?

No, it never asserts any property of the created list.

It tests that

  • The constructor of Game that takes a list of QuizQuestions works: it doesn't raise an exception or crash, and results in a game with 1 question.
  • The number of questions of a game can be accessed as game.Questions.Count

And yes, people write tests like that.

1

I think you need to separate Acceptance Test from Unit Tests.

An acceptance test would be driven by what the owner of the app wants. In your game example, we don't know what that is exactly because it's been abstracted in question list with just the one question. You may be asked to build a website that displays a 'Question of the Day.' Displaying just one question should pass this test.

The owner/client/user shouldn't tell you how to actually build this. You may work with a development team that decides the way to do this is to put a question in a list of some sort and make sure there's only one item. That is much narrower in scope. There may need to be several unit tests verifying the individual pieces of the code do what you intend them to do, AND stay that way, to complete the acceptance test.

It can be difficult for a book or example to tell you how to program or test your application. You may have to find several resources to learn how to:

  • create an object
  • put an object in a list
  • get the count of the items in a list
  • write a test that compares the count to some other value

It's up to you to put it all together.

1

"One of the things I struggle with when unit testing is deciding what behaviors to actually test"

This is the nub of the issue - it should be clear in your mind why you are testing. Unsure? Well, there are a number of common approaches/themes (N.B. not exhaustive):

Behaviour

You want to prove the code does what you think it does. In your example, yes - you'd want to check that adding to an empty list yields a single member. You (or someone else) might have hard coded this to get something going so it is a perfectly valid test.

Coverage

You might use tests to ensure that all code (or a decent proportion) has been executed at least once.

Requirements

You might have a list of specific business rules you need to satisfy. You can then write tests in a Given...When...Then style to prove they are fulfilled. If you're interested in learning more about this approach look into BDD which marries TDD with DDD IMHO.

Automation

You might have a large number of data scenarios you want to cover. Automated tests allow you to do this. Some unit testing framework such as Nunit allow you to set up random values, ranges and combinations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.