简体   繁体   中英

Many test cases to cover a function - phpunit

For the following function I need to write more test cases, I have already written one, can someone give some ideas, Perhaps to test return values of intermediate function calls.

 public function calculateShortestPath($graphObj, $start, $destination)
{
    $shortestPath = null;

    if ($this->validateParams($graphObj, $start, $destination) == true) {

        $result = $this->getAllVerticesAndNeighbours($graphObj);

        $vertices = $result[self::VERTICES];
        $neighbours = $result[self::NEIGHBOURS];

        $vertexCost = array_fill_keys($vertices, self::INFINITY);
        $visitedVertices = array_fill_keys($vertices, null);

        $vertexCost[$start] = 0;
        $vertexQueue = $vertices;

        $result = $this->getShortestPath($vertexQueue, $vertexCost, $neighbours, $visitedVertices, $destination);

        $vertexCost = $result[self::VERTEX_COST];
        $shortestPathVertices = $result[self::SHORTEST_PATH_VERTICES];

        $path = $this->getRefinedShortestPath($shortestPathVertices, $destination);

        $shortestPath = new ShortestPath($path, $vertexCost[$destination]);

    }

    return $shortestPath;
}

I have written following case already,

 /**
 * Test for calculateShortestPath function
 *
 * @param string|int $start starting point
 * @param string|int $destination destination point
 * @param array $expectedShortestPath expected shortest path
 * @param int|float $expectedCost expected cost
 * @dataProvider testCalculateShortestPathDataProvider
 */
public function testCalculateShortestPath($start, $destination, $expectedShortestPath, $expectedCost)
{
    $actualResult = $this->shortestPathCalc->calculateShortestPath($this->graph, $start, $destination);

    /* @var $actualResult ShortestPath */
    $this->assertEquals(
        $expectedShortestPath,
        $actualResult->getPath(),
        sprintf('Incorrect shortest path from %d to %d !', $start, $destination)
    );

    $this->assertEquals(
        $expectedCost,
        $actualResult->getCost(),
        sprintf('Incorrect shortest path cost from %d to %d !', $start, $destination)
    );
}

As a general rule, unit tests should exhibit two traits:

  • Each test should test one and only one thing
  • Each test should be as dumb as humanly possible

The reason for the first trait is that if a test fails it will log which test case method triggered the failure. If that method tests lots of things it makes it more of a nuisance to determine the exact failure.

The second trait exists because every time a test fails, then there must be a problem. The problem can only exist in one of two places (ignoring bugs in PHP and its extensions, or in the unit tester): in the code under test, or in the test itself. "Clever" tests will make it difficult to determine which case it is, and you don't want to spend an hour or two hunting down a bug in your class when it turns out it's actually the test that's buggy.

In your example above your current test is pretty good, but it breaks the first rule (there's two tests happening at once). Unless running the method under test is really expensive it might be worth having this test case twice, with the first run asserting the expected shortest path and the second one asserting the expected cost (and if your method does have an expensive run time then there's an incentive to try and optimise it right there :) ).

Your test may also break the second rule because I have no idea what $this -> graph is or how it's set up. Is this an actual business object or just a mockup of it? You might want to look into the mocking an stubbing capabilities of PHPUnit.

Regarding test strategies, there are two general approaches - Black box testing (where you test a unit against its specifications but treat it like you have no knowledge of its internal workings) and glass box (where you use your knowledge of the unit's internal workings to devise tests). My preferred approach it to mainly adopt a black box strategy, build tests around the specs, and then once I've got the spec fully covered move to a glass box strategy to write additional tests that will cover any code paths that the black box test doesn't exercise.

Testing is often about boundaries, as in testing responses to input when that input is both valid and invalid. So for each method your class has, you want one typical case (what's often called the "happy path") that demonstrates typical usage, a number of extreme but still valid inputs, a range of inputs that are just outside the valid range (if your method excepts numbers in the range 1-10 then a test case with 0 and a test case with 11 would cover those cases) and a test case with data wildly outside of the valid range. Many errors in programming occur at the transition between valid and invalid input (the off-by-one error is probably the most notorious example) so your tests should cover those regions thoroughly.

One nice thing about black box testing is that if you know the specs, you can write the tests before there's any code to even test. Then you can start implementing code, test it, correct it for failed tests and repeat this process until you get a 100% pass rate. This is called Test Driven Development.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM