I have a pretty basic function in my class that does exactly as the comment say, it just forwards calls to the child model if they don't exist on this class.
This works perfectly from my tests.
/**
* Handles calling methods on the user model directly from the provider
* Allows e.g. Guardian::User()->findOrFail(1) without having to redeclare
* the methods.
*
* #param $method
* #param $parameters
*
* #return mixed
*/
public function __call($method, $parameters){
$user = $this->createModel();
return call_user_func_array([$user, $method], $parameters);
}
However I also want to write unit tests for this, in which case the test I tried writing:
public function testProviderAsksModelToFind(){
$factoryUser = Factory::attributesFor('User', ['id' => 1]);
$p = m::mock('Webfox\Guardian\User\Guardian\Provider[createModel]',['']);
$user = m::mock('Webfox\Guardian\User\Guardian\User[find]');
$p->shouldReceive('createModel')->once()->andReturn($user);
$user->shouldReceive('find')->with(1)->once()->andReturn($factoryUser);
$this->assertSame($factoryUser, $p->find(1));
}
However this is spitting out the lovely error below:
1) EloquentUserProviderTest::testProviderAsksModelToFind
BadMethodCallException: Method
Webfox\Guardian\User\Guardian\Provider::find() does not exist on this
mock object
So, how do I fix this so my test passes?
The funny part is that method find is called on Provider Mock class. The error claims that. Shouldn't it be:
$this->assertSame($factoryUser, $user->find(1));
?
Related
I get an error:
Type error: Too few arguments
I thought Laravel do some magic to if arguments is not fully passed?
For example:
In the Controller I have:
public function create(CreateRequest $request)
{
return $this->todoService->createList($request);
}
In the todoService class:
use App\Plan
class todoService {
public function createList($request, Plan $plan)
{
//
}
}
As you can see I did not pass Plan class object. Do I have to bind or something?
If you are calling createList() by yourself so you will need to pass both parameters by yourself. You can bind something with Plan but still if you will call something not Laravel, then you will be responsible to pass that function parameters.
This type hinting only works if Laravel is calling that function. So you need to call these functions through Laravel.
If you are trying to automatically injecting in a class's constructor, you can simply do this:
$service = $this->app->make('App\Plan\todoservice');
or
$service = App::make('App\Plan\todoservice');
or
$service = resolve('App\Plan\todoservice');
But this will only work for Constructors. Please note that you can also provide parameters as next arguments of make() or resolve() function.
In fact, different methods can also be called that way.
You can simply do:
$service = App::make('App\Plan\todoservice');
$container->call([$service, 'createList'], ['request' => $request] );
Here $container is object of Illuminate\Contracts\Container\Container.
You have to bind classes only if they depend on interfaces. If you specify particular class, reflection will do the job for you. documentation
The only way this will work, is to set the default value of second parameter. In any other situation, syntax exception will be thrown.
use App\Plan
class todoService
{
public function createList($request, Plan $plan = null)
{
//
}
}
public function create(CreateRequest $request)
{
return $this->todoService->createList($request);
}
It will work, but will that make any sense?
Laravel cannot do any magic on this level as your coding error is simply a PHP syntax error. You're indicating that the second parameter is of type Plan, which makes it mandatory implicitly. Laravel cannot 'patch' simple function calls like this.
Your confusion is likely in that, depending on your routing, Laravel can inject the correct Plan parameter into the create controller method, thus allowing you to forward it into the service.
/**
* Create a new personal access token for the user.
*
* #param string $name
* #param array $scopes
* #return \Laravel\Passport\PersonalAccessTokenResult
*/
public function createToken($name, array $scopes = [])
{
return Container::getInstance()->make(PersonalAccessTokenFactory::class)->make(
$this->getKey(), $name, $scopes
);
}
/**
* Set the current access token for the user.
*
* #param \Laravel\Passport\Token $accessToken
* #return $this
The class under test is as follows:
class ElasticSearchInstaller
{
/**
* Version of ElasticSearch
*
* #var \ElasticSearch\Requierments
*/
protected $requirements;
/**
* Object Constructor.
*
* #param Requierments $requirements
*/
public function __construct(Requirements $requirements)
{
$this->requirements = $requirements;
}
/**
* Set requirements
*
* #return bool
*/
public function canInstall()
{
return ($this->isInstalled() && $this->requirements->checkRequirements());
}
}
The test looks as follows:
class ElasticSearchInstallerTest extends TestCase
{
/** #test **/
public function it_can_check_if_its_installable()
{
$requirements = m::mock('alias:ElasticSearch\Requirements');
$requirements->shouldReceive('checkRequirements')->once()->andReturn(true);
$installer = new ElasticSearchInstaller($requirements);
$this->assertInternalType('bool', $installer->canInstall());
}
}
Although for some reason it does not pass, as the mock says checkRequirements() is not being called and expects to be called once.
1) ElasticSearchInstallerTest::it_can_check_if_its_installable
Mockery\Exception\InvalidCountException: Method checkRequirements() from App\ElasticSearch\Requirements should be called
exactly 1 times but called 0 times.
EDIT
The problem was, within canInstall() for some reason if $this->isInstalled() is called before $this->requirements->checkRequirements() such as in the code above. It errors. if its swapped to:
public function canInstall()
{
return ($this->requirements->checkRequirements() && $this->isInstalled() );
}
...it passes! wtf?
The issue is that such boolean conditions can be shortcut -if the first part of an && (and) condition is FALSE, the whoe condition itself cannot become true, and so the rest of the parts of the conditional is skipped. The function call was skipped, hence, run 0 times.
Since, in that specific test, you only only currently testing the condition as a whole, you can mock the rest of the ElasticSearchInstaller and do much the same:
$searchInstaller->shouldReceive('isInstalled')->andReturn(true);
The mock would fall back to use the original canInstall(), and so run the condition. It's still almost a too-simple-to-fail case though - I'd concentrate elsewhere for testing.
Consider this class
class Foo {
public function alias_method($input) {
return $this->actual_method($input);
}
public function actual_method($input) {
# Do something
}
}
Now, I have already written tests that verify the behavior of actual_method, so for alias_method, all I want to ensure is that it calls actual_method with $input.
How can I do this?
As you've asked for a code-example, this is a PHPUnit test method body that has your expectation set-up already:
/**
* TODO Find out whether or not the test is necessary
*
* Note: This example is a slight adoption from the Observer/Subject
* mock example from PHPUnit docs:
* Example 9.11: Testing that a method gets called once and with
* a specified argument
*
* #link http://phpunit.de/manual/current/en/test-doubles.html
*
* #test
*/
public function methodIsCalledWithInputArgument()
{
// Create an Input object to test with as method argument
$input = new Input();
// Create a mock for the Foo class,
// only mock the actual_method() method.
$foo = $this->getMock('Foo', array('actual_method'));
// Set up the expectation for the actual_method() method
// to be called only once and with the argument $input
// as its parameter.
$foo->expects($this->once())
->method('actual_method')
->with($this->identicalTo$(input))
;
// Call the alias_method() method on the $foo object
// which we expect to call the mocked Foo object's
// actual_method() method with $input.
$foo->alias_method($input);
}
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Throw a NotImplementedError in PHP?
When writing base-classes, I often develop gradually, and only build what I need (Or, what I am testing). Yet I like my interface to be balanced. For every getter a setter. When implmenting CRUD, even when right now, I need only the R, I'd rather write the stubs for CUD laready.
I want these to throw some Not Implemented Exception. How can I raise such errors or exceptions?
For example:
class Resource {
/**
* get Issues a GET-request to Online, fetch with $this::$id
* #returns $this
*/
protected function get() {
if ($this->id) {
$attributes = http_build_query(array("id" => $this->id));
$url = "{$this->url}?{$attributes}";
$options = array_merge(array('method' => 'GET'));
$response = _http_request($url, $options);
$this->parse($response);
}
return $this;
}
/**
* post Place a new object on Online
*
* #param $attributes ....
* #returns Source $this
*/
protected function post($attributes = array()) {
throw new NotImplementedError();
}
/**
* put Updates an object on Online
*
* #param $attributes ....
* #returns $this
*/
protected function put($attributes = array()) {
throw new NotImplementedError();
}
/**
* delete Removes an object from Online, selected by $this::$id.
* #returns $this
*/
protected function delete() {
throw new NotImplementedError();
}
}
Have a look at this previous answer: Throw a NotImplementedError in PHP?
Very simply put, one doesn't exist in PHP, but you can easily create your own by extending an existing Exception. The previous answer suggests using BadMethodCallException to do this:
class NotImplementedException extends BadMethodCallException
{}
Then you can throw a NotImplementedException within your code.
As I see it you have 2 possibilities:
Derive from the exception class and implement a not implemented exception, that you can throw.
Define the not implemented methods as abstract. The compiler will then generate a error. Do not forget to mark the entire base class as abstract.
You can throw new NotImplementedException() - just like this one from the Symfony API.
I'm just beginning with PHPUnit and TDD.
Among others, I can't really answer to this question: Is this a good test? Am i actually testing my code or something already tested (i.e. the framework or PHP itself)?
Little example, this is the test subject:
class DateMax extends Constraint
{
/**
* #var string
*/
public $limit;
/**
* #var string
*/
private $invalidLimit = 'Option "limit" should be a valid date/time string.';
public function __construct($options = null)
{
parent::__construct($options);
if(false === strtotime($this->limit)) {
throw new InvalidOptionsException($this->invalidLimit, ['limit']);
}
}
}
I want to test that InvalidOptionsException is expected when invalid "limit" options are passed, otherwise $constraint->limit holds the correct value:
/**
* #dataProvider getInvalidLimits
* #expectedException InvalidOptionsException
*/
public function testInvalidLimits($testLimit)
{
new DateMax($testLimit);
}
/**
* #dataProvider getValidLimits
*/
public function testValidLimits($testLimit)
{
$constraint = new DateMax($testLimit);
$this->assertEquals($testLimit, $constraint->limit);
}
/**
* #return array[]
*/
public function getInvalidLimits()
{
return array(array('invalid specification'), array('tomorr'));
}
/**
* #return array[]
*/
public function getValidLimits()
{
return array(array('now'), array('+1 day'),array('last Monday'));
}
So question is does this make any sense or I'm testing the framework/PHP itself?
Of course it has sense, because you override constructor of Constraint class and there is possibility that you'll break something inside it. So basing on your constructor logic basically you want to test two things:
check if you call parent's constructor with the same options, exactly once (you can use mock for this purpose, you don't care about setting appropriate limit value, because this should be tested in Constraint class)
check if an appropriate exception has been thrown when limit has wrong value (eg. null)
edit: Some use case where first test will be useful may be this one:
Let say at some moment you want to extend your DateMax constructor in this way:
public function __construct($options = null)
{
$this->optionsWithDecrementedValues = $this->doWeirdThings($options);
parent::__construct($options);
if(false === strtotime($this->limit)) {
throw new InvalidOptionsException($this->invalidLimit, ['limit']);
}
}
but for example you didn't notice that method "doWeirdThings" takes a reference as argument. So in fact it changes $options value, what you didn't expect, but first test fails so you won't miss it.