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
Related
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();
}
}
I have a PhpUnit test class like this:
use PHPUnit\Framework\TestCase;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
final class SheetValidationTest extends TestCase {
private function isTitleValid(string $title) {
$worksheetStub = $this->createStub(Worksheet::class);
$worksheetStub->method('getTitle')->willReturn($title);
[ // ... act and assertion here ]
}
}
When I run this test, I get:
Cannot stub or mock class or interface "PhpOffice\PhpSpreadsheet\Worksheet\Worksheet" which does not exist
The Worksheet class is present (in vendor) and Ctrl+Click takes me to that very class.
The actual code where that class is used works fine.
A Google search and SO search did not help me to solve this problem; anybody got an idea?
Should I use the MockBuilder, instead? Thanks, folks!
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'm running into an error while trying to run some simple tests with SimpleTest for PHP.
Currently, I'm extending the class UnitTestCase as per the documentation. I'm trying to test different aspects of my class within a method. Here is my class:
<?php
class SimpleClass extends UnitTestCase {
public function __construct() {
$this->test();
}
private function test() {
$x = true;
$this->assertTrue($x, 'x is true');
}
}
I've tried extending the TestSuite class and using the syntax in the documentation but I got the same error:
Fatal error: Call to a member function getDumper() on a non-object in /simpletest/test_case.php on line 316
Any ideas on how I could do this or am I approaching class testing wrong?
Don't use a constructor in your test!
SimpleTest allows you to create classes with methods. If their name starts with "test", it is automatically recognized as a testing method that will get called if you start the test suite.
You created a constructor which calls your test method, and does an assertion without all the setup taking place, so SimpleTest does not have a reporter class that is needed to wrap it's test findings into a nice output.
Read the tutorial more closely, and you'll find some hints on how to set up a test suite or how to start a single test:
Let us suppose we are testing a simple file logging class called Log in classes/log.php. We start by creating a test script which we will call tests/log_test.php and populate it as follows...
Code example adapted from the documentation:
<?php
require_once('simpletest/autorun.php');
require_once('../classes/log.php');
class TestOfLogging extends UnitTestCase {
function testLogCreatesNewFileOnFirstMessage() {
$this->assertTrue(true);
}
}
?>
Note there is no constructor, and the autorun file will take care to run the test if this file is executed with PHP.
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