How to test a method that uses the cache variable? - php

<?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.

Related

Access static variable from "setUpBeforeClass" in "DataProvider" method in php unit test

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.

How to unit test model method that calls method on another model in Laravel

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.

Mockey/Laravel with static call of Authorizer::getResourceOwnerId

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.

PHP: Retain static methods AND maintain testability

My static methods are either of the 'helper' variety, e.g. convertToCamelCase(), or of the 'get singleton' variety, e.g. getInstance(). Either way, I am happy for them to live in a helper class.
The helper class needs to be widely available so I am loading it in my layer supertypes. Now, as far as I can see, provided that the helper can be injected into the supertypes, I have maintained full flexibility over testing my code (with the exception of the helper class itself). Does that make sense? Or am I overlooking something?
To look at it another way... it seems to me that difficulty in testing code increases in proportion to the number of calls to static methods, not in proportion to actual number of static methods themselves. By putting all these calls into one class (my helper), and replacing that class with a mock, I am testing code that is free of static calls and related problems.
(I realise that I should work towards getting rid of my Singletons, but that's going to be a longer term project).
In the case of a static class that is strictly a helper function like "convertToCamelCase" I would probably just have 100% coverage for that function and then consider it a "core" function and not worry about mocking it elsewhere. What is a mock for "convertToCamelCase" going to do anyway..? Perhaps your unit tests start to smell a little like integrations tests if you do it too much, but there's always a bit of a tradeoff between abstracting everything and making your app needlessly complicated.
As far as singletons it is tricky because you usually have the name of the static class in your code so it becomes problematic to swap it out with a mock object for testing. One thing you could do is wherever you are making your static method calls, start by refactoring to call them this way:
$instance = call_user_func('MyClass::getinstance');
Then, as you increase your testing coverage, you could begin replacing with something like:
$instance = call_user_func($this->myClassName . '::getinstance');
So - once you have that, you could swap out and mock MyClass by changing $this->myClassName. You'd have to make sure you're requiring or autoloading the relevant php files dynamically as well.
Refactoring to use an abstract factory pattern would make things even easier to test but you could start implementing that over time as well
However if you need to mock a static class somewhere, you can do it with Moka library. Here is example:
class UsersController
{
public function main()
{
return json_encode(User::find(1));
}
}
This is how you can test it:
class UsersController
{
private $_userClass;
public function __construct($userClass = 'User')
{
$this->_userClass = $userClass;
}
public function find($id)
{
return json_encode($this->_userClass::find($id));
}
}
class UsersControllerTest extends \PHPUnit_Framework_TestCase
{
public function testMainReturnsUser()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$this->assertEquals('"USER"', $controller->find(1000));
}
public function testMainCallsFind()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$controller->find(1000);
// check that `find` was called with 100
$this->assertEquals([1000], $userClass::$moka->report('find')[0]);
}
}

unit test a method that creates an object

I'm trying to get my head round Unit Testing and there's one more piece of the jigsaw I need to find.
What I'm trying to do is write tests for the following code. In this case, I've got a really simple Front Controller (written in PHP).
class frontController
{
public function routeRequest($oRequest)
{
$sClassname = $oRequest->getController();
$sMethod = $oRequest->getAction();
$oController = new $sClassname();
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
}
The problem I have is because the code creates new objects. I can easily mock the request object so that I can tightly control what it will actually do within my test case. I'm not sure the best way to actually replace the controller with a test double.
This article from IBM suggests having a factory method for creating my controller and then overriding this with a specific class used for testing:
class frontController
{
public function routeRequest($oRequest)
{
$sMethod = $oRequest->getAction();
$oController = $this->createController($oRequest);
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
protected function createController($oRequest)
{
$sClassname = $oRequest->getController();
return new $sClassname();
}
}
and then for testing perhaps something like this:
class testFrontController extends frontController
{
public function setMockController($oMockController)
{
$this->oMc = $oMockController;
}
protected function createController($oRequest)
{
return $this->oMockController;
}
}
(note this isn't quite what the article says, but I'm thinking it would be most useful to me if it did this)
Another solution could be to have another class that creates the controller. This would then be a dependent class of the frontController. This way I can replace the factory/creation class during testing with a test double. Something like this:
class frontController
{
public function routeRequest($oRequest, $oControllerFactory)
{
$sMethod = $oRequest->getAction();
$oController = $oControllerFactory->create($oRequest);
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
}
class controllerFactory
{
public function create($oRequest)
{
$sClassname = $oRequest->getController();
return new $sClassname();
}
}
I guess the dependency injection could be taken care of in the front controller constructor or via a setter instead of a parameter to the actual "route" method.
I think I prefer option 2.
Is either of these two methods the right way of going about testing this kind of thing?
(perhaps "good way" would be better word here!)
Any thoughts or suggestions on option 1 vs option 2 appreciated or indeed any alternatives. Remember - the key thing is about how to test an object that itself creates other objects as part of its execution.
Thanks!
You might find this article handy.
It discusses how object creation should be separated from the actual running of the application.
I generally find factories to be a good thing to use for this scenario. In addition to the swappability aspect, it means that additional parameters, data, or dependencies required by the object being created can be stored by the factory, and so the object which actually requests the new object doesn't have to know anything about them...
You do not want to use the real controller but a mock, right ?
It seems to me the simplest way to achieve this would be to subclass the request so that it returns the name of a MockController.
I assume you have thought through your assertions so as to define the goal of what exactly you are testing. Keep in mind that unit tests are going to be testing the returns from your methods, which, in this case, is $oResponse (whatever this may be). As a result, your test assertions will be based on this return value. Since I don't know what that return value is from your code snippets, I can only demonstrate an example that you can complete.
I would recommend PHPUnit for your testing as it seems to be the most complete package for PHP imho (many are fans of SimpleTest, as well ... to each their own).
It would look something like this (Please note that I have left out includes for brevity. Read the PHPUnit documentation for more information):
class AimTest extends PHPUnit_Framework_TestCase{
private $_controller = null;
private $_request = null;
public function setUp(){
$this->_controller = new frontController();
//what does this object's type?
$this->_request = new requestObject();
}
public function testObjectCreation(){
/*
* note, that this is only one of several assertions that could
* be made depending on the return value
*/
$return = $this->_controller->routeRequest($this->_request);
//tailor to what you expect your output to be
$this->assertTrue($return == "my expected output");
}
Hope I didn't miss the mark completely on your stated purpose. Moral of the story is that you can only test what your methods return. If you want to test object instantiation from a method, use the instanceof PHP function against a method that returns that object after instantiation.

Categories