I have an AuthorizationUpdater service, with an implementation based on Doctrine ORM. While I finished the production code, and have some tests, I was forced out of my usual TDD cycle by not knowing how to write these two:
testWhenDatabaseReadFails_exceptionIsThrown (to test this code)
testWhenDatabaseWriteFails_exceptionIsThrown (to test this code)
The Doctrine implementation of AuthorizationUpdater take a Doctine EntityManager in its constructor, which it uses to do a read and a write. These two tests are supposed to test that when either of these EntityManager methods throws a Doctine ORMException, it is caught by the service and converted to a domain exception.
My test is an integration test that gets the EntityManager to use from a factory.
Creating a simple fake via the PHPUnit mock API (getMock('className')->method('updateStuff')->willThrow(new SomeExcetion)) does not seem nice in this case. The service uses the EntityManager before the method the exception gets thrown from, so mocking out everything is not an option. This leaves creating some complex fake of EntityManager and partially mocking it (just the method that should throw an exception). The later requires providing the real constructor arguments, which my test actually does not have access to, as this responsibility lies in another package. Hence both approach are not satisfactory, and leave me preferring to have this ORMException catching behaviour to be not tested.
Is there a nice way to write the tests I'm after? Being able to monkey-patch the relevant method on the EntityManager instance would be prefect, though that'd require the runkit extension.
I'm using PHP 7, and might be able to switch to PHP 7.1 if needed.In case you're curious, the code is on GitHub, with the relevant tests residing at the end of DoctrineAuthorizationUpdaterTest.
Instead of creating a mock object through the PHPUnit mock API you could create a proxy class in your test namespace, that gets the EntityManager from the factory as constructor parameter. The proxy class passes every method call to the EntityManager, except for the calls that are to throw an exception. Most calls could be passed through with the __call magic method, except for the calls to interface methods of Doctrine\Common\Persistence\ObjectManager, which the Proxy class will probably have to implement (interface methods can't be implemented with call).
Is this proxy class the "complex fake" you are talking about and want to avoid? Then maybe you can use the Mockery library and don't have to write the class.
Related
I have the next scenario: I have to write some unit testings for controllers. In the controllers i have all types of CRUD actions. The problem is that in actions of post/patch/delete my api is communicating with a web service where i pass some data. The communication is done with guzzle.
Question: How to avoid the communication when i call a route? Is there other possibility to test the hall controller?
I assume that your microservice is a class which is registered in the Laravel's service container.
As far as I know, you can't mock classes outside the container in Laravel.
So, In case you're not doing that, register the service in the container in AppServiceProvider class. And get the microservice from the container in the controller See here for details.
In your unit test, you can make a mock object of your microservice according to your needs and inject it in the container (replacing the real one). for eg.
$this->instance(MyMicroService::class, Mockery::mock(MyMicroService::class, function ($mock) {
// here you tell the mock object which method will be called and what to return and how many times it will be called. it's totally customizable.
$mock->shouldReceive('contactRemoteServer')->once()->andReturnTrue();
}));
and it's easier if you use the mock method in your testcase class, but it does the same thing:
$this->mock(MyMicroService::class, function ($mock) {
$mock->shouldReceive('contactRemoteServer')->once()->andReturnTrue();
});
see the docs for more examples.
NB: In case you don't know, A mock class is a class made especially for the test so it doesn't affect the results and to be controllable in accordance to the test needs.
For example if you want to have a case where your microservice will throw an exception and you want to test the controller's response for that you will have to create a mock class for each unit test.
Create a sample class with the same functionality but I have one question if you are creating unit tests and you don't want to make the guzzle work so what will you test.
Please read the documentation carefully you will get the option.
In my suggestion, you can use mocking.
The Scenario
I'm writing unit tests to test an API which is currently in development stages. I have a mock container class (decorator pattern), which contains an array of mocks which will be executed in place of calls to the actual objects.
These mock containers are placed in a DI container during a test run and calls hit the container instead of the model/controllers. In most cases, we allow controllers functions through, but we occasionally want to mock them. The mock container operates by catching inaccessible function calls via __call and either returning assigned mock data or hitting the inner object.
The Problem
This works fine in the usage case of:
$this->c['Controller_Name']->functionHere()
As c['Controller_Name'] is an instance of our mock container, but the problem with this approach comes from a Controller referencing itself via $this->functionHere() when functionHere should be mocked, but the call occurs on $this which is the instance of a Controller and not our Mock Container.
Is there a plausible method for intercepting the calls to a classes own members, so I can catch $this->functionHere() and translate it appropriately into $this->c['Controller_Name']->functionHere()
Without knowing your setup its hard to talk specifics but as you are using Dependency Injection then ensuring that you interface any implementations you wish to exclude from testing will mean that you can create mock versions, mapped using a separate TestDIModule for example. That way you don't need to intercept anything.
As for catching function calls within an object - I would suggest that if you are needing to do this it may indicate that your class is performing multiple "units" of work thus could be refactored into separate classes and interface them. Extracting these out to interfaces, as described above, would allow these to be mocked and tested independently.
In my controller I'm using MyModel::where(...) which is calling the database during one of my tests. I thought that I'd be able to overload it with the following but it's still querying the database. What can I do to be sure my test is still requiring that ::where is called but returns a mocked model so I can resume testing the rest of this controller method?
MyModel::shouldReceive('where')
->once()
->with('param1', 'param2')
->andReturn($mockedModel);
First of all, you have to understand why you can't mock the where method. That's because the class Illuminate\Database\Eloquent\Model (Eloquent is its alias) doesn't have a where method explicitly declared. This method is called through the magic methods __callStatic and __call and is, in fact, a method of Illuminate\Database\Eloquent\Builder (sources).
Then, you have several options :
You can accept to call your database in your tests and just define a real context before your assertions. Your tests will be less unitary but we can deal with that in most of the cases.
#michaelcurry solution is a good one. You can build your own abstraction layer with query scopes or other architecture (injecting the query builder by yourself in the model for example) to produce a more testable code.
[never tried] You can mock directly the DB facade to completely bypass the database. You will need a good understanding of the Laravel core but it could be a good way to write "pure" unit tests.
Anyway, don't hesitate to dive into the Laravel source code. The code is clear and there are just few classes really important. That's essential to really exploit the power of the framework.
Use http://laravel.com/docs/eloquent#query-scopes and create a new function that just returns the data you want.
If you have not read it "Laravel: From Apprentice To Artisan" I would suggest it. Will help you understand how all of this is structured.
is it better to fake dependencies (for example Doctrine) for unit-tests or to use the real ones?
In a unit test, you use only ONE real instance of a class, and that is the class that you want to test.
ALL dependencies of that class should be mocked, unless there is a reason not to.
Reasons not to mock would be if data objects are being used that have no dependencies itself - you can use the real object and test if it received correct data afterwards.
Another reason not to mock would be if the configuration of the mock is too complicated - in that case, you have a reason to refactor the code instead, because if mocking a class is too complicated, the API of that class might be too complicated, too.
But the general answer: You want to always mock every dependency, every time.
I'll give you an example for the "too-complicated-so-refactor" case.
I was using a "Zend_Session_Namespace" object for internal data storage of a model. That instance got injected into the model, so mocking was not an issue.
But the internal implementation of the real "Namespace" class made me mock all the calls to __set and __get in the correct order of how they were used in the model. And that sucked. Because every time I decided to reorder the reading and writing of a value in my code, I had to change the mocking in the tests, although nothing was broken. Refactoring in the code should not lead to broken tests or force you to change them.
The refactoring added a new object that separates the "Zend_Session_Namespace" from the model. I created an object that extends "ArrayObject" and contains the "Namespace". On creation, all the values got read from the Namespace and added to the ArrayObject, and on every write, the value also gets passed to the Namespace object as well.
I now had the situation that I could use a real extended ArrayObject for all my tests, which in itself only needed an unconfigured mocked instance of "Zend_Session_Namespace", because I did not need to test whether the values were correctly stored in the session when I tested the model. I only needed a data store that gets used inside the model.
To test that the session gets correctly read and written, I have tests for that ArrayObject itself.
So in the end I am using a real instance of the model, and a real instance of the data store together with a mocked instance of "Zend_Session_Namespace" which does nothing. I deliberately chose to separate "model stuff" and "session save stuff" which had been mixed into the model class before -> "single responsibility principle".
The testing really got easier that way. And I'd say that this is also a code smell: If creating and configuring the mock classes is complicated, or needs a lot of changes when you change the tested class, it is time to think about refactoring. There is something wrong there.
Mocking should be done for a reason. Good reasons are:
You can not easily make the depended-on-component (DOC) behave as intended for your tests.
Does calling the DOC cause any non-derministic behaviour (date/time, randomness, network connections)?
The test setup is overly complex and/or maintenance intensive (like, need for external files)
The original DOC brings portability problems for your test code.
Does using the original DOC cause unnacceptably long build / execution times?
Has the DOC stability (maturity) issues that make the tests unreliable, or, worse, is the DOC not even available yet?
For example, you (typically) don't mock standard library math functions like sin or cos, because they don't have any of the abovementioned problems.
Why is it recommendable to avoid mocking where unnecessary?
For one thing, mocking increases test complexity.
Secondly, mocking makes your tests dependent on the inner workings of your code, namely on how the code interacts with the DOCs. Would be acceptable for white box tests where the implemented algorithm is tested, but not desirable for black box tests.
Im trying to unit test my controllers, but every tutorial I found on the internet says that I should use $this->dispatch("controller/action") to run the specific action I want to test. But doing it like that, I can't mock anything like data mappers, or other calls to other methods.
To solve this, I was trying to get an instance of the controller class, and then calling directly to the methods I wanted to test, but doing it like this, I get an error:
Zend_Exception: Setting request object manually is not allowed
Im not even sure this is the right approach, because I dont really know how I will test things doing it like this.
My test case: http://pastie.org/1812717
My ControllerTestCase: http://pastie.org/1812722
Any help would be appreciated.
Thank you.
You have two solutions:
Pseudo unit testing (more like acceptance testing) with Zend_Test_PHPUnit_ControllerTestCase
Creating new instance of the Controller, passing all the required dependencies (dispatcher, request, response, plugin managers etc.)
Both of them actually require dispatching process. The first, dispatching the application, second, dispatching the controllers.
See the manual and sample tests from the full Zend Framework package.
Also, look at the controller source code, to see, how the dependencies are managed.
See also other SO posts about Dependency Injection to the Zend Framework controllers.
I would guess that Zend_Test_PHPUnit_ControllerTestCase is preventing you from being able to mock Requset and Response objects. I would bypass it and just extend PHPUnit_Framework_TestCase. As I mentioned in another question, I am currently mocking Controllers without any problems.
Here is an example test that runs fine:
http://pastie.org/1816705
AbstractRestController is simply a controller class that extends Zend_Controller_Action
How is your data mapper (or other objects) instantiated? Are you instantiating it directly in the controller or grabbing it from the bootstrap/registry? If using registry or bootstrap, then place the mock in the registry/bootstrap.
If you are directly instantiating in the controller, you will need to modify your controller. Maybe have a controller method to set the data mapper, and then have another method to grab the data mapper, and if one is not set, then instantiate it. That allows your tests to inject a mock.
I typically don't mock many classes when testing controllers - I want to test the entire app and its ability to render the page... It looks like you are testing no records in the database, so why not use Zend_Test_PHPUnit_Db to set up an empty table for that test, rather than mock the data mapper to return no data?