Question on how to unit-test this kind of "big"(not that much!) method
So I have this reaaaally long method that I want to test. I could probably break it some other 4-5 methods, but I don't want to make those methods public (and although I could, I'd like to stay away, if possible, from turning them into package-protected/internal):
If my math isn't wrong, to test this as it is I'll need something like 8 tests. Dividing this in smaller methods wouldn't make me have to make less unit-tests, would it? The big problem I see with creating smaller methods(for testing) besides the visility issue is the need to pass to those small methods lots of arguments.
Although it was me who originally wrote this code, I still find it hard to understand. So my question is...how to test this?!? How would you do it? Should I restructure this in any way?
public Genoma GenerateOffspring(Genoma genoma1, Genoma genoma2) {
if (genoma1.NumberOfGenes != genoma2.NumberOfGenes) throw new ArgumentException("Both individuals must have the same size!");
if (genoma1.NumberOfGenes == 0 || genoma2.NumberOfGenes == 0) throw new ArgumentException("0-sized genomas not allowed!");
if (genoma1.NumberOfGenes == 1) {
if (numberGenerator.GenerateInteger(0, 2) == 0) {
return genoma1;
} else {
return genoma2;
}
}
int cutPoint = numberGenerator.GenerateInteger(1, genoma1.NumberOfGenes);
Genoma parent1;
Genoma parent2;
if (numberGenerator.GenerateBool()) {
parent1 = genoma1;
parent2 = genoma2;
} else {
parent1 = genoma2;
parent2 = genoma1;
}
Genoma offspring = parent1.Clone();
if (numberGenerator.GenerateBool()) {
for (int i = 0; i < cutPoint; ++i) {
offspring.SetGeneAt(i, parent2.GetGeneAt(i));
}
} else {
for (int i = cutPoint; i < offspring.NumberOfGenes; ++i) {
开发者_运维问答 offspring.SetGeneAt(i, parent2.GetGeneAt(i));
}
}
return offspring;
}
Thanks
The main thing to keep in mind is, when you start testing, you will run all tests, but for a given test, if it fails one assertion at some point, it will stop, meaning that what is after it will not be tested because it is assumed to require what was already not correctly asserted.
The good thing about dividing it into smaller methods would be that you would end up getting more things that could be tested independently, but the increase in arguments that you mention suggests that it would not be a "natural" thing to do, so you should keep it all in same test because you are, according to your words, seeing the whole function as a unit itself, and so, one unit test should test that unit.
Additionally, the increase in arguments would also make your code less pretty, and less efficient, I think.
I hope that helps.
I say there is no easy way around this one. You seem to be generating some values randomly then deciding how to set you instance variables so something like a truth table basically might help you there, in order to check for all possible values on the variables you are setting. Also I would test the if statements at the top in order to make sure that those conditions are satisfied before you proceed to the rest. There is now way to test easily, only thoroughly.
Skimming over your code, you should at least test following conditions
- genoma1.NumberofGenes = genoma2.NumberOfGenes
- genoma1.NumberofGenes <> genoma2.NumberOfGenes
- genoma1.NumberofGenes = -1
- genoma1.NumberofGenes = 0
- genoma1.NumberofGenes = 1
- genoma2.NumberofGenes = -1
- genoma2.NumberofGenes = 0
- genoma2.NumberofGenes = 1
- genoma1.NumberOfGenes = HighBound - 1
- genoma1.NumberOfGenes = HighBound (Max Integer?)
- genoma1.NumberOfGenes = HighBound + 1
- genoma2.NumberOfGenes = HighBound - 1
- genoma2.NumberOfGenes = HighBound (Max Integer?)
- genoma2.NumberOfGenes = HighBound + 1
Your testing effort could greatly benefit from mocking the numberGenerator object and have it return a fixed value.
This would give you following additional conditions to be tested
- numberGenerator.GenerateInteger = LowBound - 1
- numberGenerator.GenerateInteger = LowBound
- numberGenerator.GenerateInteger = LowBound + 1
- numberGenerator.GenerateInteger = HighBound - 1
- numberGenerator.GenerateInteger = HighBound
- numberGenerator.GenerateInteger = HighBound + 1
- numberGenerator.Generate = True
- numberGenerator.GenerateBool = False
Testing ALL possible combinations with given intputs calls for 432 testcases (6.6.6.2)
Using a pairwise testgeneration tool reduces this to 39 testcases.
When all your tests pass, you should run a coverage profiler to verify you haven't missed a coding path. Every path should get executed at least once.
- By now, you have code that has most of the bugs removed.
- By now, you have code that most likely still contains some bugs. (sad but true).
PICT Model
genoma1_NumberOfGenes: -1, 0, 1, HighboundMinOne, HighBound, HighBoundPlusOne
genoma2_NumberOfGenes: -1, 0, 1, HighboundMinOne, HighBound, HighBoundPlusOne
numberGenerator.GenerateInteger: LowBoundMinOne, LowBound, LowBoundPlusOne, HighBoundMinOne, HighBound, HighBoundPlusOne
numberGenerator.GenerateBool: True, False
PICT Generated testcase parameters
genoma1_NumberOfGenes genoma2_NumberOfGenes numberGenerator.GenerateInteger numberGenerator.GenerateBool
HighBoundPlusOne -1 HighBoundPlusOne True
HighBound 1 HighBoundPlusOne False
-1 HighBoundPlusOne HighBound True
HighBoundPlusOne 0 LowBound False
0 HighBoundPlusOne HighBoundPlusOne False
0 1 HighBoundMinOne True
-1 0 HighBoundMinOne False
HighBound 0 LowBoundPlusOne True
1 HighboundMinOne LowBound True
HighboundMinOne HighBound LowBoundMinOne False
-1 HighBound HighBoundPlusOne True
1 0 HighBound False
HighBoundPlusOne HighboundMinOne HighBoundMinOne False
HighboundMinOne HighboundMinOne LowBoundMinOne True
HighBound -1 LowBoundMinOne False
HighBoundPlusOne 1 LowBoundMinOne True
HighBoundPlusOne HighBound LowBoundPlusOne False
1 -1 HighBoundMinOne True
HighBound HighBound HighBoundMinOne False
1 HighboundMinOne HighBoundPlusOne True
HighBound HighboundMinOne HighBound False
1 1 LowBoundPlusOne False
HighBoundPlusOne -1 HighBound True
0 0 LowBoundMinOne True
1 HighBound LowBound True
0 HighBound HighBound False
HighBound HighBoundPlusOne LowBound True
-1 -1 LowBoundMinOne True
0 HighboundMinOne LowBoundPlusOne True
HighBoundPlusOne HighBoundPlusOne LowBoundPlusOne False
HighboundMinOne 1 HighBound True
-1 1 LowBound True
HighboundMinOne -1 LowBound True
HighboundMinOne -1 LowBoundPlusOne False
-1 HighboundMinOne LowBoundPlusOne False
HighboundMinOne HighBoundPlusOne HighBoundMinOne False
HighboundMinOne 0 HighBoundPlusOne False
1 HighBoundPlusOne LowBoundMinOne False
0 -1 LowBound False
精彩评论