I'm rewriting an existing Laravel 4 application to ensure that there is adequate testing. Long story short, I've rewritten my AccountController class using TDD methods and I'm running into a bit of a headache.
Consider the following method that renders a page including a list of users:
public function getIndex()
{
// build the view
//
return \View::make('account.list-users')
->with('users', \Sentry::getUserProvider()->findAll());
}
I'm using Smarty to render my views and Sentry for authentication.
Now, I want to write some tests like this:
public function test_getIndex()
{
// arrange
//
// set up some mocks here...
// act
//
$response = $this->client->request("GET", "/list-users");
// assert
//
// test for <table class="table">
$this->assertFalse($response->filter("table.table")==null, "table not found");
// test for some <a> tags for the "update" buttons
$element = $response->filter("td a")->first()->extract(array("href", "class", "_text"));
$this->assertTrue(strstr($element[0][0],"/my-update-url")!="");
$this->assertTrue(strstr($element[0][1],"btn btn-xs btn-success")!="");
$this->assertTrue(strstr($element[0][2],"Active")!="");
// test for some other markup...
}
I've been following Jeffrey Way's book Laravel Testing Decoded and written tests like the one above, and they work fine.
The headache comes up in the "set up some mocks here..." section. Specifically, the number of mocks I need to set up is ridiculous. This is because, as part of a larger web application, I'm using a View composer which adds data to the View model: the current user model, a menu structure, alert messages, news messages, the application version number, etc. I've cut out much of this by using a "bare bones" template for testing, but it's still a lot of stuff - to the point where I'm writing hundreds of lines of code to test this simple one-line method.
Is there a better way of doing this?
The way I see it, there are two ways of doing this:
A. The way I have been doing it
B. Mocking the \View::make call so that all of my template rendering is bypassed - something like this
public function test_getIndex()
{
// arrange
//
$userList = "this is a list of users";
$userProvider = Mockery::mock("\Cartalyst\Sentry\Users\Eloquent\Provider");
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
$userProvider->shouldReceive("findAll")
->once()
->andReturn($userList);
$view = Mockery::mock("\Illuminate\View\View");
\View::shouldReceive("make")
->with("account.list-users")
->once()
->andReturn($view);
$view->shouldReceive("with")
->with("users", $userList)
->once()
->andReturn($view);
$view->shouldReceive("render")
->once()
->andReturn("results");
// act
//
$response = $this->call("GET", "/list-users");
// assert
//
$this->assertResponseOk();
}
If I take this approach, the testing is much simpler and I'm only testing the code that's actually in the controller method, but then I'm not really testing everything involved in calling that route (which might be a good thing or might not - I'm not sure) and I worry that I won't get adequate coverage.
So, what's the best way of doing this: (A), (B), or something else?
Edit
There's a fair amount of confusion on my part regarding the testing of my controller method, made clearer by #TheShiftExchange's answer & comments below. I'm going to try to address the issue here, as an edit, because it gives me a little more room to discuss the question.
Consider the second example given in the answer below:
public function testMethod()
{
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
If I run this test, it will work, but it will access the database, which I was trying to avoid by mocking some stuff.
So, I could expand this test a little:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
This test will not work because, in addition to the mocks that are required for the controller method, I'll also need mocks for the code in my View composer. This code includes, among other things, $currentUser = \Sentry::getUser() (the user's name is displayed in the upper right hand corner of my application's pages).
So the code actually becomes:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
// plus a mock of ThisClass
// and a mock of ThatClass
// and a mock of SomeOtherClass
// etc.
// etc.
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
And it quickly gets out of hand.
This suggests to me that I'm doing something wrong, but I'm not sure what. I suspect that the problem stems from my uncertainty about what exactly I'm testing here.
So, after all that, the question becomes this:
What am I really trying to test when I'm testing a controller's methods?
The code in the controller's method? Or,
The whole process from request to response?
What I want to test is the first item - just the code in the controller method. The example in my question is pretty simple, but I do have some controller methods that do things like form validation or redirect based on user input - I'd like to test that code.
Maybe, rather than testing the code via $this->call(), I need to simply call the controller method directly?
As part of the Laravel framework, it includes some testing helpers. Including in these helpers are view testing helpers:
Asserting A View Has Some Data
public function testMethod()
{
$this->call('GET', '/');
$this->assertViewHas('name');
$this->assertViewHas('age', $value);
}
So you could do something like:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn('foo');
$this->call('GET', '/');
$this->assertViewHas('users', 'foo');
}
Related
I'm doing a PHPUnit on my controller and I can't seem to mock the Request right.
Here's the Controller:
use Illuminate\Http\Request;
public function insert(Request $request)
{
// ... some codes here
if ($request->has('username')) {
$userEmail = $request->get('username');
} else if ($request->has('email')) {
$userEmail = $request->get('email');
}
// ... some codes here
}
Then on the unit test,
public function testIndex()
{
// ... some codes here
$requestParams = [
'username' => 'test',
'email' => 'test#test.com'
];
$request = $this->getMockBuilder('Illuminate\Http\Request')
->disableOriginalConstructor()
->setMethods(['getMethod', 'retrieveItem', 'getRealMethod', 'all', 'getInputSource', 'get', 'has'])
->getMock();
$request->expects($this->any())
->method('get')
->willReturn($requestParams);
$request->expects($this->any())
->method('has')
->willReturn($requestParams);
$request->expects($this->any())
->method('all')
->willReturn($requestParams);
// ... some codes here
}
The problem here is that when ever I var_dump($request->has('username'); it always return the $requestParams value in which is the whole array. I'm expecting that it should return true as the username key exists in the array.
Then when I delete the username key on the $requestParams, it should return false as it does not contain the username key on the array
Its not ideal to mock Requests, but sometimes you just want to do it anyway:
protected function createRequest(
$method,
$content,
$uri = '/test',
$server = ['CONTENT_TYPE' => 'application/json'],
$parameters = [],
$cookies = [],
$files = []
) {
$request = new \Illuminate\Http\Request;
return $request->createFromBase(
\Symfony\Component\HttpFoundation\Request::create(
$uri,
$method,
$parameters,
$cookies,
$files,
$server,
$content
)
);
}
As far as I can see and understand you're telling your unit test that when you call $request->has() on your request object that it should return the $requestParams array, not true or false, or anything else.
Unless you specifically check what is send with a method call your mock doesn't actually care what is send, it just cares that it was called.
You might want to explore creating an empty request and filling it with data if that is possible in your use case as that'll let you run your unit test with more ease and less issues. This won't work in all cases.
You could include what assertions you're making in your unit test so we can see more clearly what you're running into, but as it is. It returns exactly what you're telling it to return. Even if that's not what you actually want it to return.
Mocks are used to separate your Unit-Test from the rest of your system. As such you usually tend to only check if a specific method is called to see if your code actually exits to the class you mocked and if it has the expected data you'd send along. In some extreme cases you can want to mock the system you're actually testing, but this usually indicates that your code is too dependent on other classes or it's doing too much.
Another reason to use mocks is to satisfy Type Casting constraints in your method calls. In these cases you'll usually create an empty mocked object and fill it with some dummy data your code will accept or break on to test the code.
In your case it seems you want to check if your code actually works correctly and for this I'd suggest either not mocking the request, or making specific tests where you tell it to return true, or false (test for both cases)
So something along the lines of:
$request->expects($this->any())
->method('has')
->with('username')
->willReturn(true); // or false in your next test
Edit:
As you mentioned in the comment Below you ran into the issue that you're using the has method multiple times in your code and ran into issues.
The Questions I've linked to in my response comment go into greater detail but to sum it up, you can use an inline function or the at() method to deal with multiple cases.
With at() you can supply specific iterations of the code to hit only that bit of the test. It has been mentioned that this makes your tests rather brittle as any has added before the previous ones would break the test.
$request->expects($this->at(0))
->method('has')
->with('username')
->willReturn('returnValue');
$request->expects($this->at(1))
->method('has')
->with('email')
->willReturn('otherReturnValue');
The inline function (callback) solution would allow you to customize your test to allow multiple cases and to return data as required. Unfortunately I'm not too familiar with this concept as I haven't used it myself before. I suggest reading the PHPUnit docs for more information about this.
In the end I'd still suggest not mocking the request and instead making an empty request that you'll fill with the data you want to check. Laravel comes with some impressive methods that'll let you manually fill the request with a lot of data you'd usually test against.
For example you can add data (post/get data) by using
request->add(['fieldname' => 'value'])
As a last few pointers I'd like to mention that it seems you use var_dump.
Laravel comes with two of it's own functions that are similar and quite useful in debugging.
You can use dd(); or dump();
dd(); dumps and stops the execution of code, while dump(); just outputs whatever you decide. so you could do dd($request); or dump($request); and see what the variables/class objects/etc holds. It'll even put it in a rather spiffy layout with some Javascript and such to allow you to see what's in it and such. Might want to check it out if you didn't knew it existed.
If you use request()->user() you can set user resolver. It allows you to return user you want. I had the same problem and solution for me was like this:
public function testSomething()
{
$user = User::factory()->create();
request()->setUserResolver(function() use ($user) {
return $user;
});
// Dumped result will be newly created $user
dd(request()->user());
}
A simpler answer than #Ian, if your situation is simpler:
Per https://stackoverflow.com/a/61903688/135114,
if
your function under test takes a $request argument, and
you don't need to do funky stuff to the Request—real route paths are good enough for you
... then you don't need to "mock" a Request (as in, mockery),
you can just create a Request and pass it, e.g.
public function test_myFunc_condition_expectedResult() {
...
$mockRequest = Request::create('/path/that/I_want', 'GET');
$this->assertTrue($myClass->myFuncThat($mockRequest));
}
I was running unit test on a FormRequest child class with Laravel Framework 9.3.0 and get this error:
Error : Call to a member function get() on null
/vendor/symfony/http-foundation/Request.php:676
# code failing
$customRequest->get('parameter');
As you can see in Request class, there are lot of public properties (source code):
public $attributes;
public $request;
public $query;
public $server;
public $files;
public $cookies;
public $headers;
...
This is the way i find to partially mock Request class, example below:
# test code
$this->customRequest = new CustomRequest();
$parameterBag = \Mockery::mock(ParameterBag::class);
$parameterBag->shouldReceive('get')
->with('parameter', \Mockery::any())
->andReturn(null) // anything
;
$this->customRequest->attributes = $parameterBag;
I'm trying to test a method that creates 2+ Process objects (also see API docs) using Symfony's ProcessBuilder and I've run into problems when trying to return different mock process objects. In fact, I'm a bit unsure about whether Mockery can even do this.
Is it possible to choose the value in Mockery's andReturn() based on the call chain of an object (including arguments)?
Ideally I'm looking for something like this:
<?php
$processBuilderMock
->shouldReceive('setPrefix("test")->add("-f")->getProcess')
->andReturn($testProcess);
Full example:
The following code creates a file /tmp/dummy if it does not exist. It uses the two commands test -f /tmp/dummy and touch /tmp/dummy for that (I know it's a dumb example).
<?php
class Toggler
{
public function toggleFile(ProcessBuilder $builder)
{
$testProcess = $builder
->setPrefix('test')->setArguments(array('-f', '/tmp/dummy'))
->getProcess();
$testProcess->run();
if (!$testProcess->isSuccessful()) { // (a)
$touchProcess = $builder
->setPrefix('touch')->setArguments(array('/tmp/dummy'))
->getProcess();
$touchProcess->run();
return $touchProcess->isSuccessful(); // (b)
} else {
// ...
}
}
}
To test all cases, I need to be able to mock the Process objects for the corresponding commands test and touch (here: $testProcess and $touchProcess). Ideally, the test code for this would look like this:
<?php
public function testToggleFileFileDoesNotExist()
{
$testProcess = \Mockery::mock('\Symfony\Component\Process\Process');
$testProcess->shouldReceive('isSuccessful')->andReturn(false); // (a)
$testProcess->shouldReceive('run');
$touchProcess = \Mockery::mock('\Symfony\Component\Process\Process');
$touchProcess->shouldReceive('isSuccessful')->andReturn(false); // (b)
$touchProcess->shouldReceive('run');
$builder = \Mockery::mock('\Symfony\Component\Process\ProcessBuilder');
$builder->shouldReceive('setPrefix("test")->getProcess')->andReturn($testProcess); // (c) Doesn't work!
$builder->shouldReceive('setPrefix("touch")->getProcess')->andReturn($touchProcess); // (c) Doesn't work!
$toggler = new Toggler();
$this->assertTrue($toggler->toggleFile($builder)); // see (b)
}
However, since Mockery does not allow arguments in the call chain, I'm a bit lost on how to test such a scenario. Any ideas?
This is slightly terrible, but you could try entering each step of the call chain separately and having it return the mock $builder. Which process is returned at the end would depend on the order of the calls.
$builder->shouldRecieve('setPrefix')->with('test')->andReturn($builder);
$builder->shouldRecieve('setPrefix')->with('touch')->andReturn($builder);
$builder->shouldRecieve('getProcess')
->andReturnValues(array($testProcess, $touchProcess));
This feels very messy, but it's the only way I've been able to find so far. (If you came up with anything better since posting this, I'd be curious to see.)
Using Symfony, I am displaying a table with some entries the user is able to select from. There is a little more complexity as this might include calling some further actions e. g. for filtering the table entries, sorting by different criteria, etc.
I have implemented the whole thing in an own bundle, let's say ChoiceTableBundle (with ChoiceTableController). Now I would like to be able to use this bundle from other bundles, sometimes with some more parametrization.
My desired workflow would then look like this:
User is currently working with Bundle OtherBundle and triggers chooseAction.
chooseAction forwards to ChoiceTableController (resp. its default entry action).
Within ChoiceTableBundle, the user is able to navigate, filter, sort, ... using the actions and routing supplied by this bundle.
When the user has made his choice, he triggers another action (like choiceFinishedAction) and the control flow returns to OtherBundle, handing over the results of the users choice.
Based on these results, OtherBundle can then continue working.
Additionally, OtherOtherBundle (and some more...) should also be able to use this workflow, possibly passing some configuration values to ChoiceTableBundle to make it behave a little different.
I have read about the "Controller as Service" pattern of Symfony 2 and IMHO it's the right approach here (if not, please tell me ;)). So I would make a service out of ChoiceTableController and use it from the other bundles. Anyway, with the workflow above in mind, I don't see a "good" way to achieve this:
How can I pass over configuration parameters to ChoiceTableBundle (resp. ChoiceTableController), if neccessary?
How can ChoiceTableBundle know from where it was called?
How can I return the results to this calling bundle?
Basic approaches could be to store the values in the session or to create an intermediate object being passed. Both do not seem particularly elegant to me. Can you please give me a shove in the right direction? Many thanks in advance!
The main question is if you really need to call your filtering / searching logic as a controller action. Do you really need to make a request?
I would say it could be also doable just by passing all the required data to a service you define.
This service you should create from the guts of your ChoiceTableBundleand let both you ChoiceTableBundle and your OtherBundle to use the extracted service.
service / library way
// register it in your service container
class FilteredDataProvider
{
/**
* #return customObjectInterface or scallar or whatever you like
*/
public function doFiltering($searchString, $order)
{
return $this->filterAndReturnData($searchString, $order)
}
}
...
class OtherBundleController extends Controller {
public function showStuffAction() {
$result = $this->container->get('filter_data_provider')
->doFiltering('text', 'ascending')
}
}
controller way
The whole thing can be accomplished with the same approach as lipp/imagine bundle uses.
Have a controller as service and call/send all the required information to that controller when you need some results, you can also send whole request.
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$responeFromYourSearchFilterAction = $this->container
->get('my_search_filter_controller')
->filterSearchAction(
$this->request, // http request
'parameter1' // like search string
'parameterX' // like sorting direction
);
// do something with the response
// ..
}
}
A separate service class would be much more flexible. Also if you need other parameters or Request object you can always provide it.
Info how to declare controller as service is here:
http://symfony.com/doc/current/cookbook/controller/service.html
How liip uses it:
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service
i'm trying to implement Respect/Rest in my existing CMS.
The Problem:
I would like to create a single magic route to works like: /admin/*/save, calls the * controller...
I would like to make something like this:
$r->any('/admin/*/save/*/', function($controller, $id = null) use ($r) {
return $r->dispatchClass($controller,array($id));
});
Note that i don't know which HTTP method user is using.
Actually I "solved" this problem with something like:
$r->any('/admin/*/save/*/', function($controller, $id = null) use ($tcn) {
$r = new Router;
$r->any('/admin/*/save/*/', $tcn($controller . '_save'), array($id));
return $r->run();
});
$tcn is a named function that returns the full namespace of the controller.
I know it's not a good approach.
EDIT:
This project wants to be Open Source, but it's still being created.
We're trying to transport an old project made on functional paradigm to OOP.
We are trying to learn about OOP while making an useful project.
Actuall state of the files can be found at: https://github.com/dindigital/skeleton
Alganet: The bootstrap for admin routes can be found at: https://github.com/dindigital/skeleton/blob/master/admin_routes.php
A simple controller sample: https://github.com/dindigital/skeleton/blob/master/src/app/admin/controllers/TagController.php
https://github.com/dindigital/skeleton/blob/master/src/app/admin/controllers/TagSaveController.php
I liked the Forwards and also the Factory approach... I could not decide yet.
Tricky question! That depends a lot of why are you making these routes dynamic. Can you show us some sample structure for your controllers so I can improve the answer?
Meanwhile, two native features that can help:
Forwards
You can treat the problem as an internal forward (does not make redirects). It's normally to redirect to other static routes, but you can redirect to a new one as well:
$r->any(
'/admin/*/save/*/',
function ($controller, $id) use ($tcn, $r) {
return $r->any(
"/admin/$controller/save",
$tcn($controller . '_save'),
array($id)
);
}
);
Factory Routes
Also, Respect\Rest implements factory routes. It is experimental but stable in tests:
$r->factoryRoute(
'ANY',
'/admin/*/save/*/',
'MyAbstractController',
function ($method, array $params) use ($tcn) {
return new $tcn($params[0] . '_save');
}
);
I've noticed a common pattern in some recently popular PHP libraries such as Laravel for example, where the API is heavily based on static classes and methods. A typical example of how this sort of API looks:
Logger::note('Handle routes for the welcome area of the site.');
Route::match('/welcome/:id', function ($id) {
$name = Model::from('users')->get('name')->where('id', $id);
$body = Template::body('templates/wecome.tpl', array('name' => $name));
HTTP::respond(200, $body);
}
The code seems nicely readable, and the 5 different static classes are autoloaded by Composer, so at first glance this seems like an attractive pattern to use. My question, for those more experienced in designing APIs, is does this seem like a good approach as things scale up?
For one example, I can immediately see some clunkiness on the horizon, if for example I should want multiple logs to be kept. In a non-static pattern I could do this:
$debugLog = new Logger('logs/debug.log');
$errorLog = new Logger('logs/errors.log');
Route::match('/welcome/:id', function ($id) {
$debugLog->note('Handle routes for the welcome area of the site.');
$name = Model::from('users')->get('name')->where('id', $id);
if (empty($name)) {
$errorLog->warn('Name is empty!');
}
}
But this gets difficult to share across many different methods and files.
#file1.php
Route::match('/welcome/:id', function ($id) {
$debugLog = new Logger('logs/debug.log');
$debugLog->note('Handle routes for the welcome area of the site.');
//etc
}
#file2.php
Route::match('/news', function ($id) {
$debugLog = new Logger('logs/debug.log');
$debugLog->note('Handle routes for the news area of the site.');
if ($error) {
$errorLog = new Logger('logs/errors.log');
$errorLog->warn('There is some problem: '.$error);
}
}
Now I am forced to repeat myself in order to instantiate the non-static class all over the place, which clutters up the code at least and arguably makes it harder to maintain.
But, on the other hand, it looks like the every-class-a-static approach doesn't scale easily either. Suppose I want to have multiple logs using the static API; I could try some singletons and factories...
Logger::get('debug')->note('Handle routes for the welcome area of the site.');
Logger::get('errors')->note('Danger!');
But this seems like it is just shifting the API from method names into string arguments (which could be misspelled, etc). And what if I want two different 'debug' loggers?
Either way I lean, static-based on the one side, or instance-based on the other, it seems like I run into limitations as the usage of the pattern grows.
Any advice on the best approach, given that I don't want to have to fill my application with repeated instantiation code, and yet I also want flexibility to grow with more kinds of classes, based on the existing ones?
These static classes in Laravel are actually glorified service locators.
"Syntactic sugar" they call "Facade" (not a good name but that is how Taylor calls them, do not confuse it with http://en.wikipedia.org/wiki/Facade_pattern)
Route::get('/', 'HomeController#showWelcome');
can be written as
$app['router']->get('/', 'HomeController#showWelcome');
read more: http://taylorotwell.com/response-dont-use-facades/