I am trying to use overload option of Mockery library on Laravel 5.
My current environment:
Laravel 5
Mockery 1.0
PHPUnit 7.5
I wrote this test case:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('overload:App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
According to documentation, this test should fail, but does not matter what I do, the test never fails! It always result in:
Time: 304 ms, Memory: 19.53 MB
OK (1 test, 1 assertion)
If I remove the overload: option, the test fails. So I assume that I'm not using the library's methods as expected.
The new test:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
The result:
Mockery\Exception\InvalidCountException: Method callBar(<Any Arguments>) from Mockery_0__App_FooClass should be called exactly 2 times but called 0 times.
Am I doing anything wrong? Does anyone know how to use this option properly?
Reading the page, I think this is the error you are looking for:
When Mockery overloads a class, because of how PHP works with files,
that overloaded class file must not be included otherwise Mockery will
throw a “class already exists” exception. This is where autoloading
kicks in and makes our job a lot easier.
The error that you're looking for will be caused if you remove these 2 lines from your test, which are added to the code in the second code sample, and in the third sample on the manual page:
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
This means your code wont take advantage of the psr4 autoloader or any autoloader that is in place, and will create a new autoloader for you, at the expense of speed, since it wont be using your dumped classmap and has to build it up from scratch.
If you take the two lines above out, you will get the expected error, as it will try to overload your class with a class of the same name. That class will already be autoloaded, so you get a fatal error.
So if you want to block calls to callBar, and return void, that is what your code will be doing, which is correct.
Removing overload will mean your mock is no longer effective, as you will have to pass it through a constructor to get it to work.
Update:
With your update, I can conclude that your code must be running the mocked callBar method twice (not the actual callBar method), with your mock of fooBar class using overload. When the mocked method gets called, nothing inside the real callBar method actually happens, it just registers that it was called. If you're expecting it once for example, write shouldReceive(1) and then fix the code that your test runs.
When you remove the overload, the global injection doesnt take place, so your mock never gets injected. However, your mock callBar method on the mock class is still expecting to be ran twice, so you get the error. You will need to remove the 2 mock code lines completely from your test.
Keep the # statements in, as it will help prevent the psr4 error outlined above.
This is not the way to test, you should never use overload: option... Laravel helps you with a lot of things, don't create the wheel again or try to...
Why would you need to use #runTestsInSeparateProcesses and #preserveGlobalState disabled ?
Let's say your test is like this:
namespace Tests\Unit;
use App\FooClass;
use App\Service\ServiceForTesting;
use Mockery;
use Tests\TestCase;
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock(FooClass::class);
$mock->shouldReceive('callBar')
->times(2);
$myClass = app(ServiceForTesting::class);
$myClass->run($mock);
$myClass->run($mock);
}
}
You should have a code like this:
namespace App;
class FooClass
{
public function callBar()
{
return 'Works !!!';
}
}
Now, let's say you have a standalone class (no controller or similar, you are in a unit test), you should have something like this:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
{
public function run(FooClass $class)
{
return $class->callBar();
}
}
Related
Consider the following code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
/**
* Class MyModel
* #package App\Models
* #mixin Builder
*/
class MyModel extends Model
{
public static function getGreens(): Builder
{
return (new self())->where('color', '=', 'green');
}
}
On the return statement, the PhpStorm (2020.3) complains that:
Return value is expected to be '\Illuminate\Database\Eloquent\Builder', 'MyModel' returned
And suggest to:
Change return type from '\Illuminate\Database\Eloquent\Builder' to 'MyModel'
which is weirdly incorrect (the where method does return an instance of \Illuminate\Database\Eloquent\Builder, while the IDE deduces the return type as being of MyModel type). By removing the return type, the IDE issues another warning:
Missing function's return type declaration
The code works without any problems, but the IDE shouldn't report any false warnings! How should I avoid these warnings in PhpStorm?
It's a result of not following the "best practices". The class-hierarchy of MyModel does not provide a method of where; in other words, such a method does not exist in the class-hierarchy. But! The parent class of Model does provide a magic method of __call() which gets triggered when an inaccessible method in the object context is invoked (in your case, the method of where). It essentially forwards the "call" to a new instance of \Illuminate\Database\Eloquent\Builder, that has the implementation of the requested method (it's acquired from invoking the method of newQuery()). This mechanism is not only IDE-unfriendly, but also slower.
Thus; drop the #mixin tag, and instead of utilizing the "magic methods", use "native access":
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class MyModel extends Model
{
public static function getGreens(): Builder
{
return (new self())->newQuery()->where('color', '=', 'green');
// ^^^^^^^^^^^^
}
}
As I understand (based on how Laravel works) it's because of #mixin line.
#mixin tag works similar to how PHP's native trait works. So if you have a method in a trait that returns $this / self and then use that trait in a class, then return of that method ($this/self) points to the class where it is used.
Now, Builder::where() method also returns $this or self ... but it's not actually a trait but Laravel magically makes that where() method available in this class.
And here comes the problem: that #return $this actually points to the Builder class but when used "as a trait" (because of #mixin) it gets resolved to the current MyModel class by the IDE.
You either use #mixin and live with ignoring the issue (you can use error suppression via Alt + Enter quick fix menu -- it will add a comment for IDE to tell to ignore that specific issue here) .. or remove #mixin and declare those methods differently.
AFAIK Laravel helper package should add all such Builder methods to the Model class via #method PHPDoc lines (look into that for details, go through past issues there to see how and why it does that etc, e.g. #541).
Another suggestion: try Laravel Idea plugin -- it's a PAID plugin but it makes working with Laravel code much easier and AFAIK it should cover such basic stuff.
For reference:
https://youtrack.jetbrains.com/issue/WI-1730 -- original request for documenting/supporting mixins
Outstanding #mixin tickets that can be related here (at least partially):
https://youtrack.jetbrains.com/issue/WI-34809
https://youtrack.jetbrains.com/issue/WI-49567
or try your own search, e.g. https://youtrack.jetbrains.com/issues/WI?q=mixin%20laravel
I have a class that uses Laravel's global event() function to fire off a single event when a model is changed. The only way I have been able to prevent this event from firing during unit tests is to actually namespace and declare a new event() function in the test itself and make it do nothing. It works, but it doesn't seem like a pretty solution to me. I looked into the Laravel docs and I can see some people have used Event::fake() inside the test successfully, but when I try to do it I get:
BadMethodCallException: Method Mockery_0_Illuminate_Contracts_Events_Dispatcher::until() does not exist on this mock object
I'm on Laravel 5.4. Is there a cleaner way to prevent this event from firing during a test? I really dislike the idea of declaring an empty namespaced event() function.
EDIT:
The class I am testing is a UserDomain class. In one part of the logic it invokes Laravel's global event() method:
event(new RoleChanged($this->user));
To suppress this from firing in a test I have tried Event::fake() and I have also tried using the trait WithoutEvents and its withoutEvents() method. Neither work, and the same error I mentioned above occurs both times.
You need to use Illuminate\Foundation\Testing\WithoutEvents trait in your test.
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutEvents;
use Illuminate\Foundation\Testing\RefreshDatabase;
class FooTest extends TestCase
{
use WithoutEvents;
}
I read some more docs on mocking in Laravel unit testing and I discovered that all I had to do is this at the beginning of the test:
$this->expectsEvents(RoleChanged::class);
This tells framework to acknowledge this event occurred but to not actually fire it off. Thanks everybody for your help. It led me to a workable solution.
Use Illuminate\Foundation\Testing\WithoutEvents trait, and place $this->withoutEvents() before any test.
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutEvents;
class UsersTest extends TestCase
{
use WithoutEvents;
/**
* #test
*/
public function email_is_required()
{
$this->withoutEvents();
// assertions
}
}
I write a little test (dead simple) and it didn't work. I have no idea why ? Here is the test:
<?php
namespace App\Tests\Mailer;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
class MailerTest extends TestCase
{
public function testConfirmationEmail()
{
$twigMock = $this->getMockBuilder(Environment::class)
->disableOriginalConstructor()
->getMock()
;
$twigMock->expects($this->once())->method('render');
}
}
I mock the Twig\Environment class and I test if the method render run only once. Nothing really fancy here. Here is my result:
1) App\Tests\Mailer\MailerTest::testConfirmationEmail Expectation
failed for method name is equal to 'render' when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
I don't understand why It didn't work. My test should run the render method only once, but it called 0 times. I tested it with PHPUnit 6.5.14 on Symfony 4.3.2.
Thanks everyone
My question is in three parts:
Does putting in a use statement trigger the autoloader immediately, or does it wait until the class is used? (lazy-loading)
If autoloading isn't done in a lazy-load fashion, could that negatively affect performance?
Which pattern is best to follow, and why? PhpStorm shows "Unnecessary fully qualified name..." as a code issue when the use statement isn't employed.
Here's an example class definition for a Laravel controller with a use statement:
namespace App\Http\Controllers;
use Carbon\Carbon;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
The same class without the use statement:
namespace App\Http\Controllers;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new \Carbon\Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
1) The class is autoloaded when you perform a new Class() statement.
2) see 1)
3) Which pattern is best to follow and why?:
I'd recommend to use use because you might get into a situation where you have really long namespaces and your code will become unreadable.
From the php docs:
This example attempts to load the classes MyClass1 and MyClass2 from
the files MyClass1.php and MyClass2.php respectively.
<?php
spl_autoload_register(function ($class_name) {
include $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
Namespaces are only an additional feature to organize classes.
EDIT: As #IMSoP pointed out in the comments, new is not the only time the autoloader is triggered. Accessing a class constant, static method, or static property will also trigger it, as will running class_exists.
The use statement can be thought of like a C pre-processing macro, if you're familiar with those: it rewrites the current file at compile time to let you write a short name for a long class, function, or constant name. It doesn't trigger autoloading, as it doesn't care if a class exists or not.
For instance, if you write use Foo\Bar\Baz as X, then everywhere that X is mentioned as a class name, the PHP compiler rewrites that to mention Foo\Bar\Baz instead. Only when code mentioning the class (e.g. new X, X::FOO, X::doSomething()) is actually run does it see if there really is a class Foo\Bar\Baz, and trigger the autoloader as necessary.
The common form use Foo\Bar\Baz is just shorthand for use Foo\Bar\Baz as Baz, assigning the alias Baz to the class name Foo\Bar\Baz.
As the manual points out the alias is only processed at compile time, so dynamic lookups will not use it. In the example above, class_exists('X') will return false, but you can use class_exists(X::class) to expand out the alias - the compiler will automatically substitute the full class name as a string, so at run-time, the expression will be class_exists('\Foo\Bar\Baz').
Whether use statements make your code better is therefore entirely a matter of style: the intent is that your code will be more readable without the long fully-qualified class names, but it will make no difference to how the code actually runs.
I'm trying to get Mockery to assert that a given method is called at least once.
My test class is:
use \Mockery as m;
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSetUriIsCalled()
{
$uri = 'http://localhost';
$httpClient = m::mock('Zend\Http\Client');
$httpClient->shouldReceive('setUri')->with($uri)->atLeast()->once();
}
}
As you can see, there's one test that (hopefully) creates an expectation that setUri will be called. Since there isn't any other code involved, I can't imagine that it could be called and yet my test passes. Can anyone explain why?
You need to call Mockery:close() to run verifications for your expectations. It also handles the cleanup of the mockery container for the next testcase.
public function tearDown()
{
parent::tearDown();
m::close();
}
To avoid having to call the close method in every test class, you can just add the TestListener to your phpunit config like so:
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener"></listener>
</listeners>
This approach is explained in the docs.
One thing to note from the linked docs is:
Make sure Composer’s or Mockery’s autoloader is present in the bootstrap file or you will need to also define a “file” attribute pointing to the file of the above TestListener class.
Just a sidenote: If you use Laravel: the make:test --unit generates a test class that extends the original PhpUnit Testcase class and not the included Tests\Testcase, which loads the laravel app and runs the Mockery::close(). It is also the reason why in some cases your tests fail if you use Laravel specific code (like Cache, DB or Storage) in the units you're testing.
so if you need to test units with Laravel specific code, just swap out the 'extends Testcase' and there is no need to call Mockery::close() manually