Test cases, when, what, and why?

Being new to test-based development, this question listened to me. How much is? What needs to be tested, how should it be tested, and why should it be tested? Examples are given in C # with NUnit, but I assume that the question itself is an agnostic of the language.

Here are two current examples of my own tests on a common list object (tested with strings, the initialization function adds three elements {"Foo", "Bar", "Baz"} ):

 [Test] public void CountChanging() { Assert.That(_list.Count, Is.EqualTo(3)); _list.Add("Qux"); Assert.That(_list.Count, Is.EqualTo(4)); _list[7] = "Quuuux"; Assert.That(_list.Count, Is.EqualTo(8)); _list.Remove("Quuuux"); Assert.That(_list.Count, Is.EqualTo(7)); } [Test] public void ContainsItem() { Assert.That(_list.Contains("Qux"), Is.EqualTo(false)); _list.Add("Qux"); Assert.That(_list.Contains("Qux"), Is.EqualTo(true)); _list.Remove("Qux"); Assert.That(_list.Contains("Qux"), Is.EqualTo(false)); } 

The code is pretty self-commenting, so I won’t go into what is happening, but is it too far? Add() and Remove() are tested separately, so what level should I use with these types of tests? Should I even carry out such tests?

+6
language-agnostic tdd
source share
4 answers

I would say that what you are actually testing are equivalence classes. In my opinion, there is no difference between adding to a list that contains 3 elements or 7 elements. However, there is a difference between 0 items, 1 item and> 1 item. I would probably have 3 tests for each add / remove method for these cases.

As soon as errors begin to come from QA / users, I would add each such error report as a test case; see error reproduction, having received a red strip; fix the error by getting a green bar. Each such error detection test remains there - it is my safety net (read: regression test) that even if I make this error again, I will have instant feedback.

+7
source share

Think of your tests as a specification. If your system can break (or have material errors) without test failures, then you do not have enough test coverage. If a single point of failure breaks many tests, you are probably too much (or too tightly coupled).

It is very difficult to determine in an objective way. I suppose I would say that making mistakes is too difficult. Then, when the tests start to annoy you, these are specific tests for refactoring / reprofiling (because they are too fragile or they check the wrong thing, and their failures are not useful).

+6
source share

A few tips:

  • Each test should check only one. This means that the structure of the test test should be "setup", "execute", "assert". In your examples, you mix these phases. Try breaking your test methods. This makes it easy to see exactly what you are testing.

  • Try giving your test methods a name that describes what it is testing. That is, the three test files contained in your ContainsItem () become: containsReportsFalseIfTheItemHasNotBeenAdded (), containsReportsTrueIfTheItemHasBeenAdded (), containsReportsFalseIfTheItemHasBeenAddedThenRemoved (). I find that forcing myself to come up with a descriptive name like this helps me conceptualize what I should check before I encode the actual test.

  • If you are doing TDD, you should write your first tests and only add code to your implementation if you have a failed test. Even if you do not, it will give you an idea of ​​how many tests are enough. Alternatively, use a coating tool. For a simple class such as a container, you should aim for 100% coverage.

+2
source share

Is _list instance of the class you wrote? If so, I would say that it is reasonable. Although in this case, why are you creating your own List class?

If this is not the code you wrote, do not check it unless you suspect that it somehow does not work.


I am trying to test code that is independent and modular. If there is any divine function in the code that I have to support, I select as much of it in the subfunction as possible and test them independently. Then the function of God can be written as “clearly correct” - there are no branches, there is no logic, just passing the results from one proven subfunction to another.

+1
source share

All Articles