1

Consider this example class which in encrypting some data and sending it to some other service.

Consider the test for redirect method.

This is how you consume the class:

<?php
$safeLink = new SafeLink('some_secret_key');
$safeLink->redirect('http://external.service.com/receiver.php', $someData);

and the class is smt. like:

<?php
class SafeLink
{
    public function encrypt($data)
    {
        return ....
    }

    public function redirect($url, $data)
    {
        $encryptedData = $this->encrypt($data);

        header('HTTP/1.0 302', true, 302);
        header('Location: http://example.com?' . $encryptedData, true);
        exit;
    }
}

So I have two questions:

  1. How would you test this class considering the header and the exit directives...

What I'd normally do to test this would be sending in $url and $data and get the redirected url somehow (probably by using some wrapper class for header) and test if the data is encrypted correctly.

I was experimenting with phpspec on this and I couldn't mock things and while investigating I found in this article that unit tests are not supposed to be used for testing states, that would be an integration test actually. That makes sense to me now but I'm a little bit confused...

  1. So if that's the case, what would you be testing for this redirect method, the only option I can think of is testing if it's callable with $url and $data, is that all for unit testing?
1
  • $link is undefined. and you are doing nothing with $encryptedData i think you should reexamine your example. Commented Jul 15, 2016 at 20:03

1 Answer 1

1

(I'm not too well versed in PHP, but this advice should overcome language barriers)

  1. How would you test this class considering the header and the exit directives?

  2. So if that's the case, what would you be testing for this redirect method, the only option I can think of is testing if it's callable with $url and $data, is that all for unit testing?

Since both these points boil down to "How would you test this class?", here's my approach:

As far as I can tell, the redirect method provides the following contract:

  • It will output a redirect header

  • It will redirect to a specified $link

  • It will append some encrypted $data to that link (so the receiver can use it)

  • This encryption uses the key given to the constructor

  • It will exit the program

Given these points, it should not be hard to find the values to be verified:

  • Verify, that the output contains a redirect header

  • Verify, that the output contains a Location header that points to $link

  • Verify, that the location header has some parameter appended

  • Verify, that this parameter matches the encrypted $data

  • Verify, that the execution does not continue after the call

These tests can be achieved by multiple means (e.g. mocking/hooking/... the header() function and verifying the calls, capturing the generated output and parsing the necessary information, ...).

For example:

<?php # test_callee.php
    $testSafeLink = new SafeLink($_GET['key']);
    $testSafeLink->redirect($_GET['link'], $_GET['data']);
    echo "this should not be called";
?>

<?php # test_caller.php
    $data = ...; # fill these in with test parameters
    $link = ...;
    $key = ...;
    
    $enc_data = new SafeLink($key)->encrypt($data);
    $res = file_get_contents("http://127.0.0.1/path/to/test_callee.php?link=" . urlescape($link) . "&data=" . urlescape($data) . "&key=" . urlescape($key));
    
    assert(strpos($http_response_header[0], "302") !== false);
    assert(strpos($http_response_header["Location"], $link) !== false);
    assert(strpos($http_response_header["Location"], $enc_data) !== false);
    assert(strpos($res, "this should not be called") === false);
?>

This should give you some ideas (depending on your test framework, there might be something better that assert, also, there might be some better string comparison than strpos that I'm not aware of).

3
  • Well that was my approach too, the problem is you cannot get the response headers while testing and I was trying figure it out somehow and while investigating I stumbled upon the article I mentioned. It says testing the state is not correct you should test the behaviour of the unit and not the output, which makes me confused. What do you think about that?
    – madpoet
    Commented Jul 17, 2016 at 8:08
  • you cannot get the response headers while testing: just one example (you probably don't want to use this in production though) file_get_contents("http://127.0.0.1/path/to/file.php?link=$link&data=$data&key=$key"). You get the whole response, including headers.
    – hoffmale
    Commented Jul 17, 2016 at 10:19
  • For your mention of testing behavior, that normally means testing the concrete implementation. My problem with that (for normal unit tests) is that it makes for brittle tests (even small implementation changes can break the test) and you lose an important guarantee: if your behavior test fails, you cannot be sure if the unit under test would still give the same output as before regardless whether it breaks because of a direct implementation change in the unit or in some called function. You get this guarantee if you only test for the given interface, since you only test for correct outputs
    – hoffmale
    Commented Jul 17, 2016 at 10:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.