use statement inside another class - php

I am going through a Laracast tutorial which suggest me to have the statement use DatabaseTransactions; inside my test class definition, to allow the data changes in my test case will persists only for that case.
I have a test class,
Class MyClassTest extends TestCase
{
// This use statement is responsible for
//all the data operations to persists only with in that test case
use DatabaseTransactions;
/**
* #test
*/
public function my_test_function()
{
// My test case code. Which inserts/updates
//data and assert statement.
}
}
The use DatabaseTransactions works in a way that the data changes in the method persists only with in that. How that works exactly?

As went further, DatabaseTransactions is trait, which contains the method beginDatabaseTransaction with #before annotation.
Having an use DatabaseTransactions within the test class lets the class have that method.
Since the method beginDatabaseTransaction contains a #before annotation, it will be executed before every test case.
The below is the method definition which begins a transaction and rolls back existing transaction before the test case finishes(it is still unclear why #before should be called before the test case finishes).
#before
public function beginDatabaseTransaction()
{
$this->app->make('db')->beginTransaction();
$this->beforeApplicationDestroyed(function () {
$this->app->make('db')->rollBack();
});
}

Related

How to make Mockery overload option work properly on Laravel 5

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();
}
}

PHPUnit marking test as risky when defining expectations on passed mock

I am trying to set test expectations on a mock object that is created in a data provider and passed to my test method. This is useful because I can reused my data provider across different test cases and have the tests define what to expect on the mock. However, phpunit marks this test as risky when the case passes, but correctly fails the test when it does not pass. Is this a known thing that cannot be done?
I am using phpunit v9.3
Here is a contrived example to show the problem:
<?php
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class Test extends TestCase
{
public function provideMock(): array
{
return [
[$this->createMock(\DateTime::class)],
];
}
/** #dataProvider provideMock */
public function testMockPasses(MockObject $mock): void
{
$mock->expects($this->once())->method('format')->with('Y-m-d');
$mock->format('Y-m-d');
}
/** #dataProvider provideMock */
public function testMockFails(MockObject $mock): void
{
$mock->expects($this->once())->method('format')->with('Y-m-d');
$mock->format('Y-m-');
}
}
I would expect this to work fine as I am just passing the object to the method - all internal php stuff.
Try running PHPUnit with --verbose key - it may tell more about the reason of marking the test as risky.

How to prevent Laravel event() from firing during unit tests

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
}
}

Generating a single unit test for a function inside of a controller Symfony2/PHP

I have a function that exists within a controller.
I want to that the logic is sound by writing a small unit test with assertions to match correctly with the output of the function.
I've created a new unit folder to house all the little unit tests for the controller. The correct term may be a functional test?
This is the current set up I have to house all of the assertions for the function. My question is, out of all the functions that exist in this external controller, how can I bring that function in and perform a test on the logic within it?
<?php
namespace Acme\SimplewanBundle\Tests\Unit;
use Doctrine\ORM\Tools\SchemaTool;
class ConfigControllerUnitTest extends \PHPUnit_Framework_TestCase {
public function testValidIpRange() {
}
}
First, the method should probably be moved to a service independent from the controller. In any case, without doing that change, you can already test the controller class as any other PHP class:
namespace Acme\SimplewanBundle\Tests\Unit;
use Doctrine\ORM\Tools\SchemaTool;
class ConfigControllerUnitTest extends \PHPUnit_Framework_TestCase {
public function testValidIpRange()
{
$controller = new \AppBundle\Controller\ConfigController();
$this->assertTrue($controller->isValidIpRange(...));
}
}

Mockery shouldReceive()->once() doesn't seem to work

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

Categories