I have following problem. I doing API for our web and customer must use his function as callback in my function.
Example:
UserClass {
userMethod() {
return $data;
}
}
MyClass {
myFunction (callback ) {
doingSomething();
doingSomething();
$data = call_user_function($callback);
return doingSomethingWithData($data);
}
}
Problem is, that this is API and I cant implement customer class as callback, because non exist, but i need test that function will be work with expected data. Is there any possibilites how test my function using phpunit?
Thanks a lot
Just pass in anonymous function that returns expected result the one you can predict output for. Make sure that it handles garbage data out/edge cases properly.
Your test can look something like that:
class MyClassTest extends PHPUnit_Framework_TestCase
{
/**
* #dataProvider myFunctionProvider
*/
public function testMyFunction($callback, $expected)
{
$this->assertEquals(
// Just as example you can create instance of class and call it.
MyClass::MyFunction($callback),
$expected
);
}
public function myFunctionProvider()
{
return [
[ function () { return 'a';}, 'a'],
[ function () { return 'c';}, 'c'],
[ function () { return 'b';}, 'b']
];
}
}
As a side note change your code to:
function MyFunc(callable $callback) {
}
That'll make sure you get only callable in to your function.
Related
I have the following function within an object that I wish to test
public function getUser($credentials, UserProviderInterface $userProvider)
{
$resourceOwner = $this->getAuthClient()->fetchUserFromToken($credentials);
// ... Code to create user if non-existent, update values, etc.
// ... Basically the code I want to test is here
return $user;
}
The getAuthClient() call returns a Client object with the available function of fetchUserFromToken
How can I, in a PHPUnit test, mock the fetchUserFromToken to just return a ResourceOwner object? Because the actual function does a lot of authentication mechanisms and is out of the scope of this test case
I found a php library called Runkit, but that is not the approach I want to peruse. It feels hacky and overkill for the problem at hand.
The getAuthClient() function is defined as follows
private function getAuthClient() {
return $this->clients->getClient('auth');
}
With $this->clients being defined by the constructor
public function __construct(ClientRepo $clients) {
$this->clients = $clients;
}
So, I mocked the ClientRepo and exposed the getClient() method to return a Mock of the AuthClient (regardless of input) so that I can control the return of fetchUserFromToken() call.
public function testGetUser() {
$client = $this->createMock(WebdevClient::class);
$client->expects($this->any())
->method('fetchUserFromToken')
->will($this->returnCallback(function()
{
// TARGET CODE
}));
$clients = $this->createMock(ClientRegistry::class);
$clients->expects($this->any())
->method('getClient')
->will($this->returnCallback(function() use ($client)
{
return $client;
}));
$object = new TargetObject($clients);
$result = $object->getUser(...);
// ... Assertions to follow
}
Sorry for maybe silly question, but I cannot find answer through googling.
My question is:
I created file TaskCest.php under backend\acceptance, In that file have following declaration
use yii\test\FixtureTrait;
public function fixtures() {
return ['tasks' => TasksFixture::className()];
}
I have that fixture class with data in data directory.
But when I run script I get following error:
[yii\base\ErrorException] ltrim() expects parameter 1 to be string, object given
Error is obvious, but I cant understand, in file yii2\test\FixtureTrait.php:145 I have function which expects name parameter to be string but object passed automatically [I dont call getFixture].
What's problem. Did someone faced the same?
-vvv output
Test tests/acceptance/TaskCest.php:getFixture
[yii\base\ErrorException] ltrim() expects parameter 1 to be string, object given
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Lib/Di.php:123
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Lib/Di.php:123
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:136
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:148
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:82
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Test.php:90
/home/nginx/www/planning-back/vendor/phpunit/phpunit/src/Framework/TestSuite.php:728
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/PHPUnit/Runner.php:98
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/SuiteManager.php:154
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Codecept.php:183
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Codecept.php:152
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Command/Run.php:282
/home/velaro/.config/composer/vendor/symfony/console/Command/Command.php:255
/home/velaro/.config/composer/vendor/symfony/console/Application.php:829
/home/velaro/.config/composer/vendor/symfony/console/Application.php:191
/home/velaro/.config/composer/vendor/symfony/console/Application.php:122
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Application.php:103
/home/velaro/.config/composer/vendor/codeception/codeception/codecept:34
Codeception recognize trait's methods like tests (it search all public methods of Cest-class include trait's methods and run it).
You should extract trait to another class FixtureLoader, and include it into your Cest file.
class FixtureLoader
{
use \yii\test\FixtureTrait;
public $fixtures;
public function fixtures()
{
return $this->fixtures;
}
}
abstract class ApiCest
{
/**
* #var FixtureLoader
*/
protected $fixtureLoader;
public function __construct()
{
$this->fixtureLoader = new FixtureLoader();
}
protected function fixtures()
{
return [];
}
public function _before(\FunctionalTester $I)
{
$this->fixtureLoader->fixtures = $this->fixtures();
$this->fixtureLoader->loadFixtures();
}
public function _after(\FunctionalTester $I)
{
$this->fixtureLoader->unloadFixtures();
}
}
class UserCest extends ApiCest
{
protected function fixtures()
{
return [
'users' => UserFixture::className(),
];
}
public function testSomething(\FunctionalTester $I)
{
$I->sendGET('/user');
$I->seeResponseCodeIs(200);
}
}
I'm using codeception for testing my PHP app, and there is a method I don't know how to use. It's called Stub::atLeastOnce() and, like Codeception's documentation of the Stub class says:
"Checks if a method has been invoked at least one time. If the number of invocations is 0 it will throw an exception in verify."
But when I try to use it, doesn't matter that I comment the call to User::getName() or not, the test passes.
My user class looks like this:
<?php
class User {
public function getName() {
return 'pepito';
}
public function someMethod() {
}
}
And my test method like this:
public function testStubUsage() {
// all methods that the stub impersonates must be, at least, defined
$user = Stub::make('User', array('getName' => Stub::atLeastOnce(function() { return 'Davert'; }), 'someMethod' => Stub::atLeastOnce('User::getName')));
$user->getName();
}
So, what is the usage of that function to make the test fail if User::getname() is never called?
The doc is not correct, you have to pass $this as third argument of Stub::make() for this to work properly:
public function testStubUsage() {
$user = Stub::make('User', array('getName' => Stub::atLeastOnce(function() { return 'Davert';}), 'someMethod' => function() {}), $this); // <- Here
$user->getName();
}
I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.
I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.
ExampleClass
public function do_stuff($runTwice){
$results = do_cool_stuff();
if($runTwice){
$results = 2 * do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff()
{
return 2;
}
In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.
tl;dr
I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).
Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:
class ExampleClass {
public function do_stuff($runTwice) {
$results = $this->do_cool_stuff();
if ($runTwice) {
$results = 2 * $this->do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff() {
return 2;
}
}
class TestableExampleClass extends ExampleClass {
/** Stores how many times the do_cool_stuff method is called */
protected $callCount;
function __construct() {
$this->callCount = 0;
}
function getCallCount() {
return $this->callCount;
}
/** Increment the call counter, and then use the base class's functionality */
protected function do_cool_stuff() {
$this->callCount++;
return parent::do_cool_stuff();
}
}
class ExampleClassTest extends PHPUnit_Framework_TestCase {
public function test_do_stuff() {
$example = new ExampleClass();
$this->assertEquals(2, $example->do_stuff(false));
$this->assertEquals(4, $example->do_stuff(true));
}
public function test_do_cool_stuff_is_called_correctly() {
// Try it out the first way
$firstExample = new TestableExampleClass();
$this->assertEquals(0, $firstExample->getCallCount());
$firstExample->do_stuff(false);
$this->assertEquals(1, $firstExample->getCallCount());
// Now test the other code path
$secondExample = new TestableExampleClass();
$this->assertEquals(0, $secondExample->getCallCount());
$secondExample->do_stuff(true);
$this->assertEquals(2, $secondExample->getCallCount());
}
}
I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:
class ExampleClass {
public function do_stuff($runTwice) {
if ($runTwice) {
return $this->do_cool_stuff_twice();
} else {
return $this->do_cool_stuff_once();
}
}
//...
}
Try setting a global variable prior to utilizing the class.
$IAmDeclaredOutsideOfTheFunction;
then use it to store the count and simply check it after your functions and classes have been called.
I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.
Example:
class urlDisplayer {
private $storage;
public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
public function displayUrl($name) {}
public function displayLatestUrls($count) {}
}
interface IUrlStorage {
public function addUrl($name, $url);
public function getUrl($name);
}
class MysqlUrlStorage implements IUrlStorage {
// saves and retrieves from database
}
class NonPersistentStorage implements IUrlStorage {
// just stores for this request
}
Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?
Edit: example test:
public function testUrlDisplayerDisplaysLatestUrls {
// get mock storage and have it return latest x urls so I can test whether
// UrlDisplayer really shows the latest x
}
In this test the mock should return a number of urls, however in the documentation I only how to return one value.
Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?
PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.
In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.
public function setUp()
{
parent::setUp();
$this->fixture = new UrlDisplayer(); //change this to however you create your object
//Create a list of expected URLs for testing across all test cases
$this->expectedUrls = array(
'key1' => 'http://www.example.com/url1/'
, 'key2' => 'http://www.example.net/url2/'
, 'key3' => 'http://www.example.com/url3/'
);
}
public function testUrlDisplayerDisplaysLatestUrls {
//Init
$mockStorage = $this->getMock('IUrlStorage');
$mockStorage->expects($this->any())
->method('getUrl')
->will( $this->returnCallback(array($this, 'mockgetUrl')) );
reset($this->expectedUrls); //reset array before testing
//Actual Tests
$this->assertGreaterThan(0, count($this->expectedUrls));
foreach ( $this->expectedUrls as $key => $expected ) {
$actual = $this->fixture->displayUrl($key);
$this->assertEquals($expected, $actual);
}
}
public function mockGetUrl($name)
{
$value = current($this->expectedUrls);
next($this->expectedUrls);
//Return null instead of false when end of array is reached
return ($value === false) ? null : $value;
}
Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.
In this specific case, I would suggest using the below class instead:
class MockStorage implements IUrlStorage
{
protected $urls = array();
public function addUrl($name, $url)
{
$this->urls[$name] = $url;
}
public function getUrl($name)
{
if ( isset($this->urls[$name]) ) {
return $this->urls[$name];
}
return null;
}
}
?>
Then in your unit test class you simply instantiate your fixture like below:
public function setUp() {
$mockStorage = new MockStorage();
//Add as many expected URLs you want to test for
$mockStorage->addUrl('name1', 'http://example.com');
//etc...
$this->fixture = new UrlDisplayer($mockStorage);
}