Mockery shouldReceive()->once() doesn't seem to work - 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

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

Extending the class with an interface AkeneoPimClientInterface?

How to extend generated command in Symfony with use Akeneo\Pim\AkeneoPimClientInterface ?
I have generated a command using php app/console generate:command and I got this class:
class AppTriggerBuildCommand extends ContainerAwareCommand
Then developed it to the point when I need all the categories from the API. Seamlessly it is really an easy question, how can I use AkeneoPimClientInterface in the command.
I want to use it something like this.
$categories = $this->apiClient->getCategoryApi()->all();
And the apiClient in here comes inside the _contruct metod
public function __construct(AkeneoPimClient $apiClient, AkeneoLocaleMapper $mapper) {
$this->apiClient = $apiClient;
$this->mapper = $mapper;
}
And in use
use Akeneo\Pim\AkeneoPimClientInterface as AkeneoPimClient;
But when I tried to put it inside the _construct method in a command it want to use the parent _construct and it just can't see the generated command.
Could anyone help me ?
php app/console trigger build -vvv
[Symfony\Component\Console\Exception\CommandNotFoundException]
Command "trigger" is not defined.
Exception trace:
() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:526
Symfony\Component\Console\Application->find() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:94
Symfony\Bundle\FrameworkBundle\Console\Application->find() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:190
Symfony\Component\Console\Application->doRun() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:84
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/html/iclei/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:117
Symfony\Component\Console\Application->run() at /var/www/html/iclei/app/console:27
Since you extend the ContainerAwareCommand you have access to Symfony's Service Container to fetch the client like this (you might have to replace the fully qualified class name with a service id, I'm not 100% sure):
$this->container->get('Akeneo\Pim\AkeneoPimClientInterface');
If you want to use the constructor (which I encourage you to do), you are almost there. You just have to also call the parent constructor as well:
public function __construct(AkeneoPimClient $apiClient, AkeneoLocaleMapper $mapper)
{
parent::__construct();
$this->apiClient = $apiClient;
$this->mapper = $mapper;
}
Both ways should work, but the latter allows you to move away from the ContainerAwareCommand to the more generic ContainerCommand, which will help you with Symfony 4 where services in the container will be private by default and therefore you can not just simply get them from the container like in the first version.
edit regarding the command name: You can specify the name of your command as argument to parent::__construct() and also set it via the configure() method, you need to override. In there you can just call, e.g. $this->setName('trigger-build');. Be careful not to use spaces, as Symfony will treat those as separate arguments. So trigger is the name of the command and build is the first argument you "feed" to the command.

Run custom code after Codeception suite has finished

I am aware of the _bootstrap.php file that's used to set up the testing enviroment, etc., but I'm looking for a way to run some code after the entire test suite has finished.
Note that I'm not looking for a way to run code after a single class, i.e. something like _after, but after all classes.
Is there a way to achieve this?
Actually managed to solve this myself, here's how, if anyone is interested.
I created a new helper class inside _support.
<?php
class DataHelper extends \Codeception\Module
{
public function _beforeSuite()
{
// Set up before test suite
}
public function _afterSuite()
{
// Tear down after test suite
}
}
You can then enable this as a module in any suite configuration (the .yml files), like this:
modules:
enabled:
- DataHelper
#Sacha's solution is specially useful if you want to share the same methods accross all suites.
If you're looking for a way to define the methods for a specific suite (or if you want a different method per suite), you can define those methods directly in the suite Helper class.
For instance, if you want to define a _afterSuite method for the Acceptance Suite, just go to support/AcceptanceHelper.php and define those methods there. Eg:
<?php
namespace Codeception\Module;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class AcceptanceHelper extends \Codeception\Module
{
public function _afterSuite() {
die('everything done');
}
}

Laravel 4 Add Method to Class (IoC / Namespaces)

I'm trying to figure out how to add a method to a class in a Laravel package, so that all controllers and models that call that class can access the new method. How do I replace this class in the IoC?
This is the package in question, Angel CMS. The package is my creation, so I can modify it if we need to add aliases or anything to accomplish this.
Let's say I want to add a method to this class:
vendor/angel/core/src/models/PageModule.php
Okay, so I copy the class file to here:
app/models/PageModule.php
And then I modify the copied file, adding a namespace and the desired custom_function method:
<?php namespace MyModels;
use Eloquent;
class PageModule extends Eloquent {
protected $table = 'pages_modules';
public static function custom_function()
{
return 'It works!';
}
}
As you can see, I am using the MyModels namespace here.
Then, I run a composer dump-autoload.
Next, I open up my app/routes.php and register the binding and set up a test route:
App::bind('PageModule', function($app) {
return new \MyModels\PageModule;
});
Route::get('test-binding', function() {
return PageModule::custom_function();
});
But, when visiting the test route, I always receive the same error that the method is undefined.
What am I doing wrong here? Thank you in advance for any help.
To Clarify:
I am attempting to replace the class application-wide so that all other classes (controllers/models/etc.) that call PageModule will have access to the custom_function method. Thanks.
To be honest, I'm pretty new to all this IoC, dependency inversion/injection concept too. But I think I've gone through the same struggle before. What I would do, as much as my knowledge allows, is...
Add a constructor to src/controllers/admin/AdminPageController.php:
protected $pageModule;
public function __construct(PageModule $pageModule)
{
$this->pageModule = $pageModule;
}
Then where you did $module = new PageModule in the same file. You replace it with:
$module = $this->pageModule;
The two modifications above makes use of Laravel's IoC to allow injecting a different PageModule object into your controller, instead of strictly creating PageModule in your code.
Now at this point Laravel should know that when it constructs the AdminPageController, it should create a PageModule and inject into the controller for you.
Since your controller now expects a PageModule class, you can no longer do class PageModule extends Eloquent in your app anymore, because even though the name is the same, PHP does not think that it is! You'll need to extend it:
So let's rename your app/models/PageModule.php to app/models/CustomPageModule.php, and in the file change the class to:
class CustomPageModule extends \PageModule {
Up to this point, you also have a CustomPageModule class that is a child of your package's PageModule. All you need to do now is to let Laravel knows that if any controllers ask for PageModule, it should serve the controller with your MyModels\CustomPageModule instead.
So at the top of your app's routes.php file:
App::bind('PageModule', 'MyModels\CustomPageModule');
Your AdminPageController should now be using your CustomPageModule and can use whatever public methods that are in there!
I'm expecting to be editing this answer heavily since this will be quite a long discussion. My first try at answering above isn't the best code you can write, but I hope it takes the least amount of edit to the original code, and then we can work up from there.
Or fast track by reading up articles like http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories
You probably have a alias for the PageModule facade, you should override this alias using your class \MyModels\PageModule in your app/config/app.php file.
Be careful, it seems like you are overwriting the PageModule class instead of extending it. You should probably extend the parent class instead of Eloquent.

PHPUnit and Selenium - run tests from another class

I am using PHPUnit and Selenium to test my web application.
At the moment I have 2 test classes - UserTest and PermissionsTest.
In UserTest I have methods which test that the program can successfully create a new user.
In PermissionsTest I turn certain permissions on and off and test the outcome.
For instance, I might turn the 'Create User' permission off and then test that the 'Create User' button is disabled. However, if I turn the 'Create User' permission back on, I want to test that it is possible to create a user.
All of the logic for being able to create a user is already in the UserTest class - so is there any way of running tests from the UserTest class from the PermissionsTest class?
At the moment I am trying the following code:
public function testUserPermission(){
$userTest = new UserTest();
if($this->hasPermission = true){
$userTest->testCanCreateUser();
}
}
However when I run this test, I get the error "There is currently no active session to execute the 'execute' command. You're probably trying to set some option in setup() with an incorrect setter name..."
Thanks!
It sounds to me like you're missing separation of your test implementation with its logic - I'm not talking about PHP issue but general test model.It will allow you to reuse your test components in various test cases.
You can take a look on some
material about Page Objects in PHP here or general selenium wiki.
The solution was as follows:
//instantiate and set up other test class
$userTest = new UserTest();
$userTest->setUpSessionStrategy($this::$browsers[0]);
$userTest->prepareSession();
//carry out a test from this class
$userTest->testCanCreateUser();
This works nicely. I can't see why using functionality from another test class is a bad idea in this case, because if I didn't do that I'd have to just rewrite that functionality into my new class, which seems less 'pure'...
For Selenium 1 (RC),
I made the following modifications instead (as well as applying the Page Object design pattern):
Specific Test class
//instantiate and set up other test class
$userTest = new UserTest($this->getSessionId());
//carry out a test from this class
$userTest->createUser();
//asserts as normal
$userTest->assertTextPresent();
...
Base Page Object class
class PageObject extends PHPUnit_Extensions_SeleniumTestCase {
public function __construct($session_id) {
parent::__construct();
$this->setSessionId($session_id);
$this->setBrowserUrl(BASE_URL);
}
}
Specific Page Object class
class UserTest extends PageObject {
public function createUser() {
// Page action
}
}

Categories