I'm trying to write a unit test for a controller using Zend and PHPUnit
In the code I get data from php://input
$req = new Zend_Controller_Request_Http();
$data = $req->getRawBody();
My code works fine when I test the real application, but unless I can supply data as a raw http post, $data will always be blank. The getRawBody() method basically calls file_get_contents('php://input'), but how do I override this in order to supply the test data to my application.
I had the same problem and the way I fixed it was to have the 'php://input' string as a variable that is settable at run time. I know this does not really apply directly to this question as it would require modifying the Zend Framework. But all the same it may be helpful to someone.
For example:
<?php
class Foo {
public function read() {
return file_get_contents('php://input');
}
}
would become
<?php
class Foo {
public $_fileIn = 'php://input';
public function read() {
return file_get_contents($this->_fileIn);
}
}
Then in my unit test I can do:
<?php
$obj = new Foo();
$obj->_fileIn = 'my_input_data.dat';
assertTrue('foo=bar', $obj->read());
You could try mocking the object in your unit tests. Something like this:
$req = $this->getMock('Zend_Controller_Request_Http', array('getRawBody'));
$req->method('getRawBody')
->will($this->returnValue('raw_post_data_to_return'));
Provided the $req->getRawBody() is, as you say, the same as file_get_contents('php://input')...
$test = true; /* Set to TRUE when using Unit Tests */
$req = new Zend_Controller_Request_Http();
if( $test )
$data = file_get_contents( 'testfile.txt' );
else
$data = $req->getRawBody();
Not a perfect solution, but similar to what I have used in the past when designing scripts to handle piped emails with great success.
Zend_Controller_Request_HttpTestCase contains methods for setting and getting various http request/responses.
For example:
$req = new Zend_Controller_Request_HttpTestCase;
$req->setCookie('cookie', 'TRUE');
$test = $this->controller->cookieAction($req);
$this->assertSame($test, TRUE);
Related
I'm writing an unit test for my PHP project,
the unit test is to simulate a php://input data,
and I read the manual, it says:
php://input is a read-only stream that allows you to read raw data
from the request body.
How do I simulate the php://input, or write the request body in my PHP?
Here's my source code and unit test, both are simplified.
Source:
class Koru
{
static function build()
{
// This function will build an array from the php://input.
parse_str(file_get_contents('php://input'), $input);
return $input;
}
//...
Unit Test:
function testBuildInput()
{
// Trying to simulate the `php://input` data here.
// NOTICE: THIS WON'T WORK.
file_put_contents('php://input', 'test1=foobar&test2=helloWorld');
$data = Koru::build();
$this->assertEquals($data, ['test1' => 'foobar',
'test2' => 'helloWorld']);
}
Use a test double
Given the code in the question, the simplest solution is to restructure the code:
class Koru
{
static function build()
{
parse_str(static::getInputStream(), $input);
return $input;
}
/**
* Note: Prior to PHP 5.6, a stream opened with php://input could
* only be read once;
*
* #see http://php.net/manual/en/wrappers.php.php
*/
protected static function getInputStream()
{
return file_get_contents('php://input');
}
And use a test double:
class KoruTestDouble extends Koru
{
protected static $inputStream;
public static function setInputStream($input = '')
{
static::$inputStream = $input;
}
protected static function getInputStream()
{
return static::$inputStream;
}
}
The test method then uses the test double, not the class itself:
function testBuildInput()
{
KoruTestDouble::setInputStream('test1=foobar&test2=helloWorld');
$expected = ['test1' => 'foobar', 'test2' => 'helloWorld'];
$result = KoruTestDouble::build();
$this->assertSame($expected, $result, 'Stuff be different');
}
Avoid static classes if possible
Most of the difficulties with the scenario in the question are caused by the use of static class methods, static classes make testing hard. If at all possible avoid the use of static classes and use instance methods which allows solving the same sort of problem using mock objects.
See vfsStream package and this SO question and answers.
Basically, you would want to parametrize your service that reads data to accept a path:
public function __construct($path)
{
$data = file_get_contents($path); // you might want to use another FS read function here
}
And then, in a test, provide an vfsStream stream path:
\vfsStreamWrapper::register();
\vfsStream::setup('input');
$service = new Service('vfs://input')
In your code you would provide php://input as per usual.
This sort of extreme decomposition gains nothing and leads very brittle code. Your tests should express the expectations of your interfaces, and not the data you've supplied them with: Is PHP truly not free to return ["test2"=>"helloWorld","test1"=>"foobar"] in some future version? Is your code broken if it does? What exactly do you think you are testing?
I think you're overcomplicating this.
$a->doit should take $input as an argument and not call Koru::build as part of its initialisation. Then you can test $a->doit instead of testing parse_str.
If you insist on pressing on this example, then Koru::build needs to take an argument of 'php://input' – this is often called dependency injection, where you tell your functions everything they need to know. Then, when you want to "test" things, you can simply pass in some other file (or e.g. a data url).
With Kahlan you can monkey patch the file_get_contents function directly like so:
use My\Name\Space\Koru;
describe("::build()", function() {
it("parses data", function() {
allow('file_put_contents')->toBeCalled()->andRun(function() {
return 'test1=foobar&test2=helloWorld';
});
expect(Koru::build())->toBe([
'test1' => 'foobar',
'test2' => 'helloWorld'
]);
});
});
Use a Zend\Diactoros\Stream
https://zendframework.github.io/zend-diactoros/usage/
$_POST['foo'] = 'bar';
use Zend\Diactoros\ServerRequestFactory;
$psrRequest = ServerRequestFactory::fromGlobals();
var_dump($psrRequest->getParsedBody()); // foo => bar
var_dump($_POST); // foo => bar
more info https://laracasts.com/discuss/channels/general-discussion/psr-7?page=1
during unit testing i'm always get confused about what to test.
Do i need to test the API and only the API or also the method result values.
class SomeEventHandler
{
public function onDispatch (Event $event)
{
if ($event->hasFoo)
{
$model = $this->createResponseModel('foo');
}
else
{
$model = $this->createResponseModel('bar');
}
// End.
return $model;
}
private function createResponseModel ($foo)
{
$vars = array(
'someVare' => true,
'foo' => $foo
);
// End.
return new ResponseModel($vars);
}
}
So should i test if the method onDispatch returns a instance of ResponseModel or should i also test if the variable foo is set properly?
Or is the test below just fine?
class SomeEventHandlerTest
{
// assume that a instance of SomeEventHandler is created
private $someEventHandler;
public function testOnDispatch_EventHasFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = true;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
public function testOnDispatch_EventHasNoFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = false;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
}
If you were checking the code by hand what is it that you would check? Just that a ResponseModel was returned or that it also had the proper values?
If you weren't writing tests and executed the code what would you look for to ensure that the code was doing what it was supposed to. You would check that the values in the returned object were correct. I would do that by using the public API of the object and verify that the values are right.
One idea is to have the tests such that if the code were deleted, you would be able to recreate all the functionality via only having the tests. Only checking the returned object could result in a function that just has return new ResponseModel();. This would pass the test but would not be what you want.
In short, what you decide to test is subjective, however you should at the minimum test all your public methods.
Many people limit their tests to public methods and simply ensure code coverage on the protected/private methods is adequate. However, feel free to test anything you think warrants a test. Generally speaking, the more tests the better.
In my opinion you should certainly test for your response data, not just the return type.
I rely on Unit Tests to let me make code changes in the future and be satisfied my changes have not created any breaks, just by running the tests.
So in your case, if the "foo" or "bar" response data is important, you should test it.
That way if you later change the response strings by accident, your tests will tell you.
How do I get started with mocking a web service in PHP? I'm currently directly querying the web API's in my unit testing class but it takes too long. Someone told me that you should just mock the service. But how do I go about that? I'm currently using PHPUnit.
What I have in mind is to simply save a static result (json or xml file) somewhere in the file system and write a class which reads from that file. Is that how mocking works? Can you point me out to resources which could help me with this. Is PHPUnit enough or do I need other tools? If PHPUnit is enough what part of PHPUnit do I need to check out? Thanks in advance!
You would mock the web service and then test what is returned. The hard coded data you are expecting back is correct, you set the Mock to return it, so then additional methods of your class may continue to work with the results. You may need Dependency Injection as well to help with the testing.
class WebService {
private $svc;
// Constructor Injection, pass the WebService object here
public function __construct($Service = NULL)
{
if(! is_null($Service) )
{
if($Service instanceof WebService)
{
$this->SetIWebService($Service);
}
}
}
function SetWebService(WebService $Service)
{
$this->svc = $Service
}
function DoWeb($Request)
{
$svc = $this->svc;
$Result = $svc->getResult($Request);
if ($Result->success == false)
$Result->Error = $this->GetErrorCode($Result->errorCode);
}
function GetErrorCode($errorCode) {
// do stuff
}
}
Test:
class WebServiceTest extends PHPUnit_Framework_TestCase
{
// Simple test for GetErrorCode to work Properly
public function testGetErrorCode()
{
$TestClass = new WebService();
$this->assertEquals('One', $TestClass->GetErrorCode(1));
$this->assertEquals('Two', $TestClass->GetErrorCode(2));
}
// Could also use dataProvider to send different returnValues, and then check with Asserts.
public function testDoWebSericeCall()
{
// Create a mock for the WebService class,
// only mock the getResult() method.
$MockService = $this->getMock('WebService', array('getResult'));
// Set up the expectation for the getResult() method
$MockService->expects($this->any())
->method('getResult')
->will($this->returnValue(1)); // Change returnValue to your hard coded results
// Create Test Object - Pass our Mock as the service
$TestClass = new WebService($MockService);
// Or
// $TestClass = new WebService();
// $TestClass->SetWebServices($MockService);
// Test DoWeb
$WebString = 'Some String since we did not specify it to the Mock'; // Could be checked with the Mock functions
$this->assertEquals('One', $TestClass->DoWeb($WebString));
}
}
This mock may then be used in the other functions since the return is hard coded, your normal code would process the results and perform what work the code should (Format for display, etc...). This could also then have tests written for it.
I have an interesting problem and have searched the internet, but haven't yet found an answer.
I work for a company that doesn't allow it's workers to utilize OOP, it is kind of ridiculous, but the working experience is valuable.
Consider the following function:
function get_setting_values_from_file( $parameter )
{
exec("/usr/var/binary --options $parameter", $output, $return);
$settings = file( $output[0] );
foreach( $settings as $setting ) {
if( strstr( $setting, "color") ) {
$setting = explode( ":", $setting );
return $setting[1];
}
}
return false;
}
I need to unit test a similar function. I am currently using phpUnit for my tests and the vfsStream libraries to mock the file system, but how do you mock the call to exec("/usr/var/binary --options $parameter", $output, $return) when I'm developing with no access to the actual system? What is the recommend approach for dealing with test cases like this?
All feedback is appreciated.
You could mock exec() by using a function mock library. I made one (php-mock) for you which requires you to use namespaces
namespace foo;
use phpmock\phpunit\PHPMock;
class ExecTest extends \PHPUnit_Framework_TestCase
{
use PHPMock;
public function testExec()
{
$mock = $this->getFunctionMock(__NAMESPACE__, "exec");
$mock->expects($this->once())->willReturnCallback(
function ($command, &$output, &$return_var) {
$this->assertEquals("foo", $command);
$output = "failure";
$return_var = 1;
}
);
exec("foo", $output, $return_var);
$this->assertEquals("failure", $output);
$this->assertEquals(1, $return_var);
}
}
Simply mock this function to return the text that you are trying to get into $settings. You do not need to call the executable, simply create the file or return.
For instance, assuming the function get_setting_values_from_file() returns the settings as an array, you can simply mock the function in your test to return the settings as an array. Create a test stub to mock the object that contains the get_setting_values_from_file() method, and have that mock simply return the same FALSE, 1 or 2 that the test assumed.
$stub = $this->getMock('GetSettingsClass');
$stub->expects($this->any())
->method('get_settings_from_file')
->will($this->returnValue(0));
This is from the PHPUnit manual -> http://phpunit.de/manual/3.8/en/test-doubles.html#test-doubles.stubs
Optionally, you could even bypass the call, and simply test the functions/code that works on the returns by creating the array and passing it to those functions.
Assumed Example in the main code:
...
$settings = get_setting_values_from_file( 'UserType' );
$UserType = get_user_type($settings);
return $UserType;
function get_user_type($settings)
{
if($settings !== FALSE) // Returned from your function if parameter is not found
{
switch($settings)
{
case 1:
return 'User'; // Best to use Constants, but for example here only
break;
case 2:
return 'Admin';
break;
...
}
}
else
{
return FALSE;
}
}
Now, in your test, you can simply
$this->assertFalse(get_user_type(FALSE, 'Ensure not found data is handled properly as FALSE is returned');
$this->assertEqual('User', get_user_type(1), 'Test UserType=1');
$this->assertEqual('Admin', get_user_type(1), 'Test UserType=2');
...
These work as the code does not call the function that had to mock the read from the OS, but does handle all the expected returns by calling the function processing the setting return value. Here, you have simply assumed the return from the function 'get_setting_values_from_file()' without needing the file or any mocks.
This does NOT however test reading from the file, which I would do in another test by using the setUp and tearDown to actual create a file with the values you want (fopen/fwrite) and then call your function and ensure it returns what is expected.
I hope this helps to explain what I was thinking.
I have this class to send a SOAP-request (the class also defines the header)
class Personinfo
{
function __construct() {
$this->soap = new SoapClient('mysource.wsdl',array('trace' => 1));
}
private function build_auth_header() {
$auth->BrukerID = 'userid';
$auth->Passord = 'pass';
$auth->SluttBruker = 'name';
$auth->Versjon = 'v1-1-0';
$authvalues = new SoapVar($auth, SOAP_ENC_OBJECT);
$header = new SoapHeader('http://www.example.com', "BrukerAutorisasjon", // Rename this to the tag you need
$authvalues, false);
$this->soap->__setSoapHeaders(array($header));
}
public function hentPersoninfo($params){
$this->build_auth_header();
$res = $this->soap->hentPersoninfo($params);
return $res;
}
}
The problem is that there's something wrong with my function and the response is an error. I'd like to find out what content I am sending with my request, but I can't figure out how.
I've tried a try/catch-block in the hentPersoninfo-function that calls $this->soap->__getLastRequest but it is always empty.
What am I doing wrong?
Before I ever start accessing a service programmatically, I use SoapUI to ensure that I know what needs sent to the service, and what I should expect back.
This way, you can ensure the issue isn't in the web service and/or in your understanding of how you should access the web service.
After you understand this, you can narrow your focus onto making the relevant SOAP framework do what you need it to do.