开发者

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜