2

I have created a class with a function that makes a request to an external site using file_get_contents()

class MyClass {

    public function emailExternal($email, $url){

        $url = $url;
        $data = array('email' => $email);

        $options = array(
            'http' => array(
                    'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
                    'method'  => 'POST',
                    'content' => http_build_query($data),
            ),
        );
        $context  = stream_context_create($options);

        $result = $this->makeExternalRequest($url, $context);

        return $result;

    }

    private function makeExternalRequest($url, $context){

        return json_decode(file_get_contents($url, false, $context), true);
    }

}

I have removed some of the function so i am only exposing what is required to understand my issue (This is why the function doesn't really do much)

I need to test the emailExternal function but need to mock the return of the makeExternalRequest function. Just so i can simulate a successful/not-successful response.

This is my attemot so far:

public function testEmailFromExternalSiteInvalidEmail(){

    $results = [
            'message' => 'test'
    ];

    $stub = $this->getMockBuilder('MyClass')
            ->setMethods(['makeExternalRequest'])
            ->getMock();
    $stub->expects($this->once())
            ->method('makeExternalRequest')
            ->withAnyParameters()
            ->will($this->returnValue($results));

    $stub->emailExternal(['email' => 'test@test.co.uk'], 'http://test.com');

}

The above works fine if the makeExternalRequest function is public or protected but no longer works whilst it is private. Its required i keep this function private but is also required that i test. Does anyone know of anything i could do?

Neil
  • 766
  • 1
  • 8
  • 23
  • Possible duplicate of [Mock private method with PHPUnit](http://stackoverflow.com/questions/5937845/mock-private-method-with-phpunit) – Jens A. Koch Dec 15 '15 at 15:22
  • I have read through this question and none of the answers seem correct. Most advise to use `$method->setAccessible(true);` with a reflection class but this isn't really what im looking for and is incorrect as you can only do this to protected method and not private. the other solution mentioned is what i already have and this again only works for protected functions – Neil Dec 15 '15 at 15:27
  • Its simple: final, private and static methods cannot be stubbed or mocked. You can only apply hacks to this problem.. Use a test helper class to extend the class, then add a "public" wrapper function, which calls the private makeExternalRequest in the parent. – Jens A. Koch Dec 15 '15 at 15:30
  • Would the function still not need to be protected for the helper class to access it? – Neil Dec 15 '15 at 15:38
  • Sorry, my bad. It will only work with protected. -- Maybe pull the json_decode() part out, that would allow to test the message. And only file_get_contents() remains. -- The cleanest approach is to drop the mock idea and focus on providing a webservice to serve $url. Then file_get_contents() is always requesting against your local server and gets test data as return. (makeExternalRequest to localhost during testing) – Jens A. Koch Dec 15 '15 at 15:53
  • Thank you for your help. I think this will be the approach i will take – Neil Dec 15 '15 at 16:48
  • Glad i could help a bit. :) – Jens A. Koch Dec 15 '15 at 16:50

1 Answers1

1

If what you ask about is true:

I need to test the emailExternal function but need to mock the return of the makeExternalRequest function. Just so i can simulate a successful/not-successful response.

then I have good news. As the public method returns the state from the remote call (that is inside the private method) directly, you only need to mock the pbulic call which can be easily done as outlined in the phpunit manual. You can make that method return anything you want then so that you can do your simulations.

This is actually like you already did, but just on the wrong method. Just mock the public method named emailExternal.

You normally do not need to mock private (or non-public) methods in unit-tests and actually you can not mock privates at all as the mock object will extend from the class that is mocked and privates are invisible from any extended class.

See as well the very informative reference Q&A we have on site for this topic:

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836