I am attempting to use Mockery to fully test some features of a Laravel 5.1 codebase.
I've not done a huge amount of unit testing in Mockery, and certainly not a huge amount with static calls... of which Laravel uses plenty.
I am aware that testing static calls is horrible.. but it can be done. In what I consider good practice (please feel free to correct me), I am attempting to mock these classes to increase separation of tested components.
I am using the \Mockery::mock('alias:{classname}') feature in order to catch these static calls.
For example, to check the current logged in user, the id is found using:
$user_id = (int)Authorizer::getResourceOwnerId();
However, the testing features seem to have some limitations, and I could be using them wrong - the exact question follows below:
Testing structure
My current situation is as follows:
class AuthRoutesTest extends TestCase {
// ...
protected $mock_authorizer;
// ...
public function setUp()
{
parent::setUp();
// ...
$this->mock_authorizer
->shouldReceive('getResourceOwnerId')
->andReturnValues(
[null, 3, 3, 3]
);
}
public function tearDown()
{
\Mockery::close();
}
Now the tests - all of these require $user_id = (int)Authorizer::getResourceOwnerId(); to return values.
public function testFirst() {
// $user_id is null, then 3, 3 finally 3
}
And here's the problem:
I want to have.. lets say testSecond() with a separate set of values: say [1,2,null,4].
Each test seems to reset the array (go back to the beginning).
Note: -
I have tried to call ->shouldReceive('getResourceOwnerId') again - this does not help
I have tried to append the next values to the array, but it starts again at key 0
Is this possible, and if so, how is this done?
(I am aware there are other ways of coding a site.. this is more about testing existing code and avoiding the re-write - at least for now)
If you add byDefault() to your expectation it's possible to override the expectation after it's initiated in the setUp() method.
You would do something like this:
public function setUp()
{
parent::setUp();
// ...
$this->mock_authorizer
->shouldReceive('getResourceOwnerId')
->andReturnValues(
[null, 3, 3, 3]
)->byDefault();
}
Then in your second test case it's then possible to override the expectation you defined in the setUp method like this:
public function testSecond() {
$this->mock_authorizer
->shouldReceive('getResourceOwnerId')
->andReturnValues(
[1,2,null,4]
);
}
This post: Override Mockery return values might also be useful.
You can also read about expectations and the byDefault() method in the Mockery docs.
Also, I would suggest that you create a facade or use dependency injection for the Authorizer class instead of creating an instance mock, (using Mockery alias) since instance mocks might affect tests in other classes and thereby result in unwanted issues.
Related
I am writing my phpunit test. I need to initialize a dataprovider method to group one of my test. But I am not able to get the static variables declared in the setUpBeforeClass method. Those variables are returning null to me.
When did a google search, the documentation said that the dataprovider methods will execute before setUpBeforeClass.
Is there any way to override this and make the static variable access from dataprovider?
Sample code
setupbefore class function
public static function setUpBeforeClass()
{
self::sampleVariable = 'Sample Data';
}
dataprovider function
public function sampleDataProvider()
{
echo self::sampleVariable;
}
The reason for data providers to be decoupled from test case setup flow is that they can be defined as external class methods. Also it is about injecting data into tests. Consider such code:
class SampleTest extends PHPUnit_Framework_TestCase
{
public function __construct($name = null, array $data = array(), $dataName = '') {
parent::__construct($name, $data, $dataName);
var_dump('constructed');
}
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
var_dump('beforeClass');
}
public function setUp(){
var_dump('before');
}
/**
* #dataProvider sampleDataProvider
*/
public function testMethod($expected, $actual) {
var_dump('test');
$this->assertEquals($expected, $actual);
}
public function sampleDataProvider() {
var_dump('dataProvider');
return [
[1, 1],
[2, 2],
];
}
}
when running it, you will notice that first SampleTest instance constructed, then dataProvider is invoked, then two instances of SampleTest created (one test method times two datasets from dataprovider), then beforeClass method runs, then test method is run twice having datasets from provider injected.
It is not quite clear what your particular use case is but is might be possible to refactor the case above into smth like:
class AnotherSampleTest extends PHPUnit_Framework_TestCase
{
public function testMethod() {
foreach ($this->sampleDataProvider() as $dataSet) {
list($expected, $actual) = $dataSet;
$this->assertEquals($expected, $actual);
}
}
public function sampleDataProvider() {
return [
[1, 1],
[2, 2],
];
}
}
here sampleDataProvider is just a regular method so you are in charge how and when to invoke it, and anyway it gets invoked after setUpBeforeClass and setUp so it has access to any stuff set up or modified there, so you can make any adjustments needed.
Downside of such approach is that you actually lose splitting one test method into multiple. So it will be one test with multiple asserts comparing to multiple tests with only a few asserts per each. Also you might need to take some extra care about setting up (I mean you might need to refresh smth before each iteration) - solvable by calling setUp manually (at first glance).
Don't think it is possible to make any further suggestions without some details on your particular use case.
try to declare the variable sampleVariable as
static, and check.
class MyClass
{
private static $sampleVariable;
}
The way PHPUnit handles data provider methods is weird. They're not called from the same object that the tests are run by, so in the object the DP call is made to, setupBeforeClass will not have been run. It's bloody daft IMO, but I'm sure there's a reason it's been done the way it has been done (xmike explains it in his answer actually. Not so daft after all ;-).
Given your assignment is just a static value (in yer example anyhow), can you not simply declare it as a class property instead of setting it via setUpBeforeClass? I guess the situation might be more complicated that your example demonstrates that prevents this approach: I have only your example to base my suggestions on.
Another thing do do would be to abstract the logic from setUpBeforeClass into a separate method, and call that method from both sampleDataProvider, and setUpBeforeClass.
I'm new to testing and writing testable code, and am looking for some clarification on the correct way to handle this simple scenario. I've read other questions and answers on SO with similar titles but they do not seem to offer a clear answer to what I'm asking.
I have a controller that calls the shipped() method on an instance of my Picking class:
class MyController extends \BaseController {
public function controllerMethod() {
$picking = new Picking;
$picking->shipped($shipmentData);
}
}
The Picking model looks like this:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped($shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$this->order->pickingShipped();
}
}
As you can see, this shipped() method saves some data, and then calls the pickingShipped() method, on it's related Order.
Now, I am trying to write a test for the shipped() method, and I'm not sure the appropriate way to do this. I've read about mocking, but I am confused if this is a situation where mocking is necessary. I've thought of a few possible solutions, but I'm not sure if any of them are correct.
1) Rearrange the code so that the controller calls the pickingShipped() method allowing it to be removed from the shipped() method, simplifying the test.
For example, the last line of the shipped() method would be removed, and the controller code would change to:
$picking = new Picking;
$picking->shipped($shipmentData);
$picking->order->pickingShipped();
2) In the test, use a mock method on order so that the test can simply confirm that the pickingShipped() method gets called.
Something along the lines of what's explained here. That would mean the test could do something like this:
$order->expects($this->once())->method('pickingShipped')
However, I think that would mean that I also need to inject the order dependency rather than relying on the order relationship within the shipped() method, like this:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped(Order $order, $shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
}
And then the code in the controller would have to look like this:
$picking = new Picking;
$picking->shipped($picking->order, $shipmentData);
This feels a little strange, but I'm really not sure what's right.
My question is, what is the proper way to write and test this code? It's easy to test the the shipped() method sets the appropriate data on itself, but what about that call to pickingShipped() at the end? This seems to make the testing more complicated. So should the code be rearranged? If so, how? Or, is this a common use-case for mocking like I outlined in the 2nd option? If so, is it correct to inject the dependency as I'm showing?
I'm not a PHP dev so this might come down to language features being a blocker.
I would suggest that the dependency injection method is better because it calls out the dependency and would allow you to separate your persistence and behavior later. For instance the Picking or Picker might be a better behavior name whilst PickingRecord might be nice for the data.
In any case if you can set default arguments in PHP then I like the last method you used (injection) and you could currently simplify to something like
public function shipped($shipmentData, Order $order = $this->order) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
This then would allow you to ignore the order dependency in production code and inject a double or other type of object as an order in tests and simply assert that the method was called on the order object. Integration tests should continue to monitor that the interfaces still mesh together even though you're injecting doubles in your unit tests.
This would be how I'd attempt to do this in Ruby.
I came up with a solution that I feel good about. It seems pretty obvious now that I see it. All I did was set the $picking->order property to return the mocked order for the test.
$order = Mockery::mock(Order::class);
$picking = new Picking;
$picking->order = $order;
$order->shouldReceive('pickingShipped')
->with($picking)
->once();
$picking->shipped($shipmentData);
Now when the shipped() method calls $this->order, it gets the mocked $order object I defined, and the test works correctly.
This feels like the right solution.
<?php
use yii\db\ActiveRecord;
class Model extends ActiveRecord
{
protected static $ids = [];
public static function getIds()
{
if (empty(static::$ids)) {
static::$ids = static::find()->select('id')->column();
}
return static::$ids;
}
}
How to use the test to make sure that the query is executed once by repeatedly calling this method ?
Preferably using codeception or phpunit.
Tests are not just a way to ensure your code works, they also help identify code smells. In your case writing a test is hard, because you use static methods.
There used to be a staticExpects method but that was deprecated in phpunit long ago, so that's not really feasible. The best way to make this code testable is to remove the static keyword. This is easy for getIds() but since the static find() is defined by a 3rd party (yii's ActiveRecord) you can't really remove it. Instead you could wrap it in a non-static method. This gives you the benefit of being able to move away from the Active Record to some other implementation like Doctrine in the future, by just touching these small methods wrapping the 3rd party code.
Once you do this you could create a partial mock of your model to make sure that method is called:
class Model extends ActiveRecord
{
private $ids;
protected function findIds()
{
return static::find()->select('id')->column();
}
public function getIds()
{
if (empty($this->ids)) {
$this->ids = $this->findIds()
}
return $this->ids;
}
}
and in your test:
public function testFindIdsIsCalledWhenGetterIsNotInitialized()
{
$model = $this->getMockBuilder(Model::class)
->setMethods(['findIds'])
->getMock();
$model->expects($this->once())
->method('findIds')
->will($this->returnValue([1, 2, 3]));
$ids = $model->getIds();
$this->assertEquals([1, 2, 3], $ids);
}
This should have 2 assertions, one for the expected method call and one for the returned values. This test bypasses the Active Record and only ensures that your getIds() method works as expected. Another way to approach this is, as mentioned in the comments to your question, to use a functional test that actually tests the database interactions by fetching the data from a (test) database. Obviously since this requires having a database connection and retrieving test data, e.g. from some previously setup fixtures, it's a bit more work and the test will be slower. Depending on how big your project is that might not be an issue and you might feel more comfortable testing the logic in the Active Record implementation as well.
Let's say I want to test a simple helper that takes a class name as an argument and makes a redirection.
How am I supposed to test this if the function is called in many places from inside a couple of controllers? Should I test every class name that was passed as a parameter in the whole code (write them in the provider function myself)? Or is there a magical function which does that for me?
Your question is the exact reason why dependency injection -- when done correctly (not how most popular frameworks "implement" it) -- is touted as the ultimate in code testability.
To understand why, lets look at how "helper functions" and class-oriented programming make your controllers difficult to test.
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
The entire point of unit testing is to verify that "units" of code work in isolation. If you can't isolate functionality, your tests are meaningless because their results could be tainted by the behavior of the other code involved. This can result in what statisticians call Type I and Type II errors: basically, this means you can get test results that might be lying to you.
In the code above, the helper cannot be easily mocked to determine that MyController::doSomething works in complete isolation from outside influences. Why not? Because we can't "mock" the behavior of the helper method to guarantee our doSomething method actually adds 100 to the helper result. We're stuck with the helper's exact behavior (returning 42). This is a problem that correct object-orientation and inversion of control eliminate entirely. Let's consider an example of how:
If MyController asks for it's dependencies instead of using the static helper function , it becomes trivial to mock the outside influences. Consider:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
Now, it's trivially simple to test that MyController::doSomething does in fact add 100 to whatever it gets back from the answer machine:
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
This example also demonstrates how the correct use of interfaces in your code can greatly simplify the testing process. Test frameworks like PHPUnit make it very easy to mock interface definitions to perform exactly what you want them to in order to test the isolated functionality of code units.
So I hope these very simple examples demonstrate how powerful dependency injection is when it comes to testing your code. But more importantly, I hope they demonstrate why you should be wary if your framework of choice is using static (just another name for global), singletons, and helper functions.
You cannot test each possible combination of parameters to all the functions you need to test; it will take you longer than the life of the universe. So you use Human Intelligence (some might call it cheating ;-). Test it just once, in this case with a mock controller as the parameter.
Then look at your code and ask yourself if any other object passed in is really going to have it behave differently. For something you describe as a "simple helper" maybe the answer is no. But, if yes, how? Create another mock controller class that simulates that different behaviour. E.g. this second controller might not have the function your helper class expects to call. You expect an exception to be thrown. Create the unit test for that.
Repeat until satisfied.
I'm having a problem with a line of code like:
$user->has('roles', ORM::factory('role', array('name' => 'unverified')))
I can mock the first argument, but can only assert that the 2nd argument returns a class. In some classes I make multiple uses of has and need to be able to test them properly. To be clear, I need to confirm that "unverified" was passed into the factory method of the 2nd argument. Any help is much appreciated.
The class I'm testing:
<?php
/**
* Policy class to determine if a user can upload a file.
*/
class Policy_File_Upload extends Policy
{
const NOT_LOGGED_IN = 1;
const NOT_VERIFIED = 2;
public function execute(Model_ACL_User $user, array $extra = NULL)
{
if ($user->can('login'))
{
return self::NOT_LOGGED_IN;
}
elseif ($user->has('roles', ORM::factory('role', array('name' => 'unverified'))))
{
return self::NOT_VERIFIED;
}
return TRUE;
}
}
and the corresponding test:
public function test_guest()
{
// User mock
$user = $this->getMock('Model_User', array('can', 'has'), array(), '', FALSE);
$user->expects($this->any())->method('can')->with('login')->will($this->returnValue(TRUE));
$user->expects($this->any())->method('has')->with('roles', $this->logicalOr
(
))
->will($this->returnCallback(array($this, 'roles')));
$policy = new Policy_File_Upload;
$this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));
}
You can always go the data-driven route of preparing test data for each case and verify the result of calling the function. In the end, you don't so much care that 'unverified' gets passed to ORM::factory() but rather that the correct result comes out of execute() based on the input.
Testing the implementation details makes the test brittle and difficult to write. If you can test the results instead, your tests won't break when you change how you arrive at the result. It may take longer to put together a data set for all your use cases, but it can generally be shared among tests.
To be clear, I need to confirm that "unverified" was passed into the factory method of the 2nd argument.
<?php
public function execute(Model_ACL_User $user, array $extra = NULL)
[...] ORM::factory('role', array('name' => 'unverified')) [...]
I'm sorry mate, you can't do that as there is no (sane) way to mock out a static method call.
The argument to why this is a problem are similar to one of Sebastian Bergmanns blog post: Testing-Code-That-Uses-Singletons
There are options like runkit that you could choose to replace the "ORM" class at runtime with a mocked version but thats really painfull.
So there are a couple of options:
You could create something like a Policy_File_Upload_Data_Provider that has a ->getRoles method where you put your ORM access. That would allow you to test your business logic nicely and testing that data access class should be a little easier as it doesn't to that much (just accessing the orm).
You could Inject your ORM class (or if that doesn't work out maybe just its name).
Depending on the class its self you should be able to create an instance and call ->factory like it would be a normal method. You could mock that.
If you don't want that just leaving that in there is also a not SO bad option.
Hope that at least gave you an idea/overview.
Additional Links why it's hard to test statics:
Misko Hevery - Flaw: Brittle Global State & Singletons
Kore Nordmann - Static considered harmful
You can actually do verification on parameters passed to mock methods utilizing parameter capturing in the Phake mocking framework for PHP.
public function test_guest()
{
// User mock
$user = Phake::mock('Model_User');
when($user)->can('login')->thenReturn(FALSE);
when($user)->has('roles', $this->anything())->thenGetReturnByLambda(array($this, 'roles'));
$policy = new Policy_File_Upload;
$this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));
Phake::verify($user)->has('roles', Phake::capture($factoriedRole));
$this->assertEquals('unverified', $factoriedRole->get('name'));
}
This example is assuming that you have the ability to check the name on $factoriedRole, obviously that piece needs to be changed depending on how that ORM stack works.
A small note, I agree with edorian regarding statics. If possible I would always prefer redesigning your code to not require statics, but sometimes that isn't possible, in which case this is a workable solution.
Phake Github Page
Phake Documentation on Parameter Capturing