views:

104

answers:

5

Example class in pseudocode:

class SumCalculator
  method calculate(int1, int2) returns int

What is a good way to test this? In other words how should I describe the behavior I need?

test1: canDetermineSumOfTwoIntegers

or

test2: returnsSumOfTwoIntegers

or

test3: knowsFivePlusThreeIsEight

Test1 and Test2 seem vague and it would need to test a specific calculation, so it doesn't really describe what is being tested. Yet test3 is very limited.

What is a good way to test such classes?

+9  A: 

I would test the boundary conditions (max-int, min-int, zero, positive, negative) and some typical cases:

test1: sumOfPosAndPos
test2: sumOfPosAndNeg
test3: sumOfPosAndZero
test4: sumOfNegAndZero
test5: sumOfMaxIntAndMinInt

etc.

Cayle Spandon
Maybe add sumOfMaxIntAndMaxInt and check for the function to report the failure. This is the most boundary case I can think of.
pmr
sumOfMaxIntAndOne should cause an exception as well, shouldn't it? That would be another good edge case.Otherwise I agree. Just think of a couple of possibilities that differ slightly and go with that.
Anne Schuessler
Don't forget about the negative test cases test6: sumOfStringAndPositive ( should raise exception or return error )
Jonas Söderström
+4  A: 

There are several philosophies. Roy Osherove, author of The Art of Unit Testing, seems to prefer using explicit values, and selecting the lowest (or simplest) representation of each Equivalence Class.

That principle doesn't apply itself particularly well to your example, but works really well in many other scenarios.

If, for example, a class requires an input of a positive integer, you pick the number 1 because it's the simplest representation of all positive intergers.

Personally, I rather prefer a principle I call Constrained Non-Determinism. The point here is that we let some kind of factory serve us anonymous variables for the given type, because it forces us to establish the relationship directly in the test.

I'm using AutoFixture to do this (but you could also use something else), so in this case I would test the SumCalculator like this:

var fixture = new Fixture();
var int1 = fixture.CreateAnonymous<int>();
var int2 = fixture.CreateAnonymous<int>();
var expectedResult = int1 + int2;
var sut = fixture.CreateAnonymous<SumCalculator>();

var result = sut.Calculate(int1, int2);

Assert.AreEqual(expectedResult, result);

In principle, this single test provides a specification for the Calculate method. We never know what the values of int1 and int2 are, and that is very appropriate in all those many cases where it actually doesn't matter.

Mark Seemann
+3  A: 

if you're testing mathematical function, i would suggest you should test it against its inverse function, for example: for function that do x = a + b, you should test it whether a-x = -b and x-b = a, this is just for illustration, ofcourse it won't work on every case.

uray
+1  A: 

Another alternative here would be to used a Parameterized Test Case to remove the duplication in the tests. Basically a table contains all the data for the tests, in a tuple form ([term1, term2, sum]), then a testcase iterates on the table on invoke the parameterized testcase to test a row in the table :

I would also add negative (overflow here) testing : what is calculate(MAXINT, 1) supposed to return ?

philippe
+1  A: 

See David Saff's work on Theory Tests; here (PDF) is an example. This is basically a way of making an assertion that something (like a function being the inverse of its function) is true for all values in some set (including the set of all possible values) - and expressing that assertion as a test. You can do some fun stuff by running your test with randomly selected values (if the set is too large to run exhaustively), and automatically recording the failures as specific concrete regression tests.

Carl Manaster