I'm new to PHPUnit and wondering is it possible to write a test for which ignore specific method.
The code is like examine whether $data is Valid or not, and if it find irregular data, send message to slack with it.
My question is, is it possible to run a test without sending alert message, like ignore sendAlert function?
If possible, I want to know how to write it, If not, I want know why and how to make this code testable.
Thanks!!
example code )
public static function isValid($data) {
// some code here
if (Valid) {
return true;
} else {
// some code here to find irregular
if (irregular) {
self::sendAlert($data);
}
return false;
}
}
private static function sendAlert($data) {
// send alert to slack
Example_Model_Slack::post($slackMsg, $channel);
}
<?
class Example_Model_Slack
{
public static function post($text, $channel = '') {
// make $params from $text and $channel
// POST
$stream = [
'http' => [
'method' => 'POST',
'protocol_version' => 1.1,
'content' => http_build_query($params),
],
];
return file_get_contents(self::POST_URL, false, stream_context_create($stream));
}
}
Edit after the question edit
If your code is in a namespace (which should be, it's good practice), it's extremely easy:
Create a new function in a separate file that is only included by your UnitTest file. This file should have the same namespace as your code. In this example, Example_Model_Slack is in the namespace Foobar\Models.
<?php
namespace Foobar\Models;
function file_get_contents(string $filename, bool $use_include_path = false, resource $context = ?)
{
return 'Whatever you want';
}
When you call a function, the code looks for it:
In the specifically used functions.
In the same namespace.
In the built-in functions.
Therefore, your code will use the built-in file_get_contents (namely \file_get_contents), but your test will use the one in the same namespace (namely \Foobar\Models\file_get_contents).
Original answer
The easiest would be to actually call sendAlert, but to mock the call to its content. As you didn't provide the code of that method, I can't be more precise, juste browse through the doc and figure it out by yourself or, alternatively, show us the code.
For a theorectical and general answer: your sendAlert method probably uses one that is provided by an external vendor, let's say \SlackApi\Slack::send($message). In that case, you could mock the provided \SlackApi\Slack class to replace the send method with one that doesn't actually send anything but still returns the expected data.
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.
In PHP I would like to know what will be the method called by SOAP. Here is a sample to understand...
$soapserver = new SoapServer();
$soapserver->setClass('myClass');
$soapserver->handle();
What I would like to know is the name of the method that will be executed in handle()
Thank you !!
In my opinion, the cleanest and most elegant way to access the called operation's name in this situation would be using some kind of Wrapper or Surrogate design pattern. Depending on Your intent You would use either the Decorator or the Proxy.
As an example, let's say We want to dynamically add some additional functionality to our Handler object without touching the class itself. This allows for keeping the Handler class cleaner and, thus, more focused on its direct responsibility. Such functionality could be logging of methods and their parameters or implementing some kind of caching mechanism. For this We will use the Decorator design pattern. Instead of doing this:
class MyHandlerClass
{
public function operation1($params)
{
// does some stuff here
}
public function operation2($params)
{
// does some other stuff here
}
}
$soapserver = new SoapServer(null, array('uri' => "http://test-uri/"));
$soapserver->setClass('MyHandlerClass');
$soapserver->handle();
We'll do the following:
class MyHandlerClassDecorator
{
private $decorated = null;
public function __construct(MyHandlerClass $decorated)
{
$this->decorated = $decorated;
}
public function __call($method, $params)
{
// do something with the $method and $params
// then call the real $method
if (method_exists($this->decorated, $method)) {
return call_user_func_array(
array($this->decorated, $method), $params);
} else {
throw new BadMethodCallException();
}
}
}
$soapserver = new SoapServer(null, array('uri' => "http://test-uri/"));
$soapserver->setObject(new MyHandlerClassDecorator(new MyHandlerClass()));
$soapserver->handle();
If You want to control the access to the Handler's operations, for instance, in order to impose access rights use the Proxy design pattern.
I know this is an old post, but someone could make use of this solution. It should be possible to extract data from raw HTTP POST data. You cannot use $_POST, because it's empty but you can use predefined variable $HTTP_RAW_POST_DATA which contains string with SOAP request in XML format.
The method name should be in first node of <soapenv:Body> tag like this:
<!--
...
XML header, SOAP header etc.
...
-->
<soapenv:Body>
<urn:methodName soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<param1 xsi:type="xsd:string" xs:type="type:string" xmlns:xs="http://www.w3.org/2000/XMLSchema-instance">param1 value</param1>
<param2 xsi:type="xsd:string" xs:type="type:string" xmlns:xs="http://www.w3.org/2000/XMLSchema-instance">param2 value</param2>
</urn:methodName>
</soapenv:Body>
<!--
...
-->
You could probably parse it with something like SimpleXML or maybe use some regular expresion to get methodName but remember that the string urn: is a namespace defined in the header and therefore it could be anything.
Although not the nicest way, you can use this http://danpolant.com/use-the-output-buffer-to-debug-a-soap-server/ somehow.
For the quick and very dirty approach (please use this only for a one-time debug and not in production code!): just assign a global variable with the name of each SOAP method in the method bodies and do whatever you want with it after the SoapServer does its job, as described in the above link. Something like this (untested code):
$method = "";
class test
{
function call1()
{
global $method; $method = "call1";
}
}
ob_start();
$soapserver = new SoapServer();
$soapserver->setClass('test');
$soapserver->handle();
$mystring = ob_get_contents(); // retrieve all output thus far
ob_end_clean (); // stop buffering
log($mystring); // log output
log($method); // log method
echo $mystring; // now send it
Usually (but not always, depends on the client) $_SERVER['HTTP_SOAPACTION'] is set and you can get the name of the called method from it.
I'm using PHP 5.3's class_alias to help process my Symfony 1.4 (Doctrine) forms. I use a single action to process multiple form pages but using a switch statement to choose a Form Class to use.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
class_alias('MyFormPage1Form', 'FormAlias');
break;
...
}
$this->form = new FormAlias($obj);
}
This works brilliantly when browsing the website, but fails my functional tests, because when a page is loaded more than once, like so:
$browser->info('1 - Edit Form Page 1')->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end()->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end();
I get a 500 response to the second request, with the following error:
last request threw an uncaught exception RuntimeException: PHP sent a warning error at /.../apps/frontend/modules/.../actions/actions.class.php line 225 (Cannot redeclare class FormAlias)
This makes it very hard to test form submissions (which typically post back to themselves).
Presumably this is because Symfony's tester hasn't cleared the throughput in the same way.
Is there a way to 'unalias' or otherwise allow this sort of redeclaration?
As an alternate solution you can assign the name of the class to instantiate to a variable and new that:
public function executeEdit(sfWebRequest $request) {
$formType;
switch($request->getParameter('page')) {
case 'page-1':
$formType = 'MyFormPage1Form';
break;
...
}
$this->form = new $formType();
}
This doesn't use class_alias but keeps the instantiation in a single spot.
I do not know for sure if it is possible, but judging from the Manual, I'd say no. Once the class is aliased, there is no way to reset it or redeclare it with a different name. But then again, why do use the alias at all?
From your code I assume you are doing the aliasing in each additional case block. But if so, you can just as well simply instantiate the form in those blocks, e.g.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
$form = new MyFormPage1Form($obj);
break;
...
}
$this->form = $form;
}
You are hardcoding the class names into the switch/case block anyway when using class_alias. There is no advantage in using it. If you wanted to do it dynamically, you could create an array mapping from 'page' to 'className' and then simply lookup the appropriate class.
public function executeEdit(sfWebRequest $request) {
$mapping = array(
'page-1' => 'MyFormPage1Form',
// more mappings
);
$form = NULL;
$id = $request->getParameter('page');
if(array_key_exists($id, $mapping)) {
$className = $mapping[$id];
$form = new $className($obj);
}
$this->form = $form;
}
This way, you could also put the entire mapping in a config file. Or you could create FormFactory.
public function executeEdit(sfWebRequest $request) {
$this->form = FormFactory::create($request->getParameter('page'), $obj);
}
If you are using the Symfony Components DI Container, you could also get rid of the hard coded factory dependency and just use the service container to get the form. That would be the cleanest approach IMO. Basically, using class_alias just feels inappropriate here to me.
function class_alias_once($class, $alias) {
if (!class_exists($alias)) {
class_alias($class, $alias);
}
}
This doesn't solve the problem itself, but by using this function it is ensured that you don't get the error. Maybe this will suffice for your purpose.
I am trying to use AMF PHP to pass variables to a flash file, thus far I cannot see anything wrong with my code, but I have very little experience with creating classes, so here it goes, here is my code,
index.php:
<?php
include "amfphp/services/flashMe.php";
$session = true;
if ($session == true) {
$uid = '12345';
$thing = new flashMe;
$thing->push($uid);
} else {
//login
}
?>
flashMe.php:
<?php
class flashMe {
public function __construct() {
}
public function push($one)
{
return $one;//sends the uid to the flash file?
}
}
?>
Flash is looking for the flashMe class and the push method within that class, but I keep getting null variables in my flash file when I run it, is there something wrong with this code?
Thanx in advance!
Your index.php file is unnecessary.
Your second file is incomplete. Here is the example from the docs for their "hello world" class file:
<?php
class HelloWorld
{
function HelloWorld()
{
$this->methodTable = array
(
"say" => array
(
"access" => "remote",
"description" => "Pings back a message"
)
);
}
function say($sMessage)
{
return 'You said: ' . $sMessage;
}
}
?>
This file should be saved as "HelloWorld" matching the "class HelloWorld" you have named in the php file (you did this part right with FlashMe).
The example file in the docs for the Flash piece (in actionscript) is here:
import mx.remoting.*;
import mx.rpc.*;
import mx.remoting.debug.NetDebug;
var gatewayUrl:String = "http://localhost/flashservices/gateway.php"
NetDebug.initialize();
var _service:Service = new Service(gatewayUrl, null, 'HelloWorld', null , null);
var pc:PendingCall = _service.say("Hello world!");
pc.responder = new RelayResponder(this, "handleResult", "handleError");
function handleResult(re:ResultEvent)
{
trace('The result is: ' + re.result);
}
function handleError(fe:FaultEvent)
{
trace('There has been an error');
}
The gateway URL should go to wherever your services can be reached. I'm sure if you try a few you'll find the right one. The neat thing about amfphp is that it allows you to also test your services out before you try implementing them in the gateway (if you go to the URL in your browser).
I'm pretty new to AMFPHP as well, but I've found the docs to be extraordinarily useful. If you need more help on classes, you can find more info on the PHP docs page.
You missed the parenthesis after new flashMe
$thing = new flashMe();
$thing->push($uid);
Amfphp or Zend AMF only allow you to call public methods on a remote class that is exposed by your gateway. You example is not a class and therefore no remote method can be called. This looks more like something that you would do with an http post.
http://framework.zend.com/manual/en/zend.amf.server.html