We have Laravel 5 controllers method:
public function getInput()
{
$input = \Request::all();
$links = $input['links'];
$this->startLinks = explode("\n", $links);
return $this;
}
How can we test this single method? How to pass POST request with data to this method? And how to create instance of this controller class in my test method?
This looks like a method which probably shouldn't belong in a controller. If you are serious about testing, I'd highly recommend reading up on the repository pattern. When testing, it will make your life a lot easier to have everything abstracted out of the controllers.=
With that said though, this is still very testable. The main idea is to figure out what needs to be tested and ONLY test that. This means we don't care what the dependencies are doing, only that they are doing and returning something which the rest of the method will need. In this case, it's the Request facade.
Then you want to make sure the variables are set appropriately and that the method returned an instance of that class. It actually ends up being pretty straight forward.
Should look something like this...
public function testGetInput()
{
$requestParams = [
'links' => "somelink.com\nsomeotherlink.com\nandanotherlink.com\ndoesntmatter.com"
];
// Here we are saying the \Request facade should expect the all method to be called and that all method should
// return some pre-defined things which we will use in our asserts.
\Request::shouldReceive('all')->once()->andReturn($requestParams);
// Here we are just using Laravel's IoC container to instantiate your controller. Change YourController to whatever
// your controller is named
$class = App::make('YourController');
// Getting results of function so we can test that it has some properties which were supposed to have been set.
$return = $class->getInput();
// Again change this to the actual name of your controller.
$this->assertInstanceOf('YourController', $return);
// Now test all the things.
$this->assertTrue(isset($return->startLinks));
$this->assertTrue(is_array($return->startLinks));
$this->assertTrue(in_array('somelink.com', $return->startLInks));
$this->assertTrue(in_array('nsomeotherlink.com', $return->startLInks));
$this->assertTrue(in_array('nandanotherlink.com', $return->startLInks));
$this->assertTrue(in_array('ndoesntmatter.com', $return->startLInks));
}
I think you are looking for this.
If your test class extends TestCase you will get a lot of helper methods which will do heavy lifting for you.
function testSomething() {
// POST request to your controller#action
$response = $this->action('POST', 'YourController#yourAction', ['links' => 'link1 \n link2']);
// you can check if response was ok
$this->assertTrue($response->isOk(), "Custom message if something went wrong");
// or if view received variable
$this->assertViewHas('links', ['link1', 'link2']);
}
Codeception extends this functionality even further.
Related
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.
Imagine we have a Request object and a Controller object. The Controller object is constructed with a Request object, like so:
abstract class Controller {
public $request;
public function __construct(Request $request)
{
$this->request = $request;
}
}
As you can see, this is an abstract class, so in reality a subclass of Controller will be constructed. Let's imagine the code is something like this:
// Instantiate the request.
$request = new Request($params);
// Instantiate the registration controller.
$controller = new RegistrationController($request);
Now let's say that we add a dependency to our RegistrationController, like so:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(Request $request, UserRepo $user_repo)
{
parent::__construct($request);
$this->user_repo = $user_repo;
}
}
At this point, what I'd like to do is introduce a dependency injection container to automatically inject the dependencies via the constructor. For this, I've been using PHP-DI. Usually, this would go something like so:
// Instantiate the registration controller.
$controller = $container->get('RegistrationController');
This would then instantiate RegistrationController with an instance of Request and an instance of UserRepo. It'd know to construct with those objects thanks to reflection, but if I wanted I could also override this via a configuration file.
The problem is that the Request object takes a parameter which is dynamic. I need the Request object passed to RegistrationController to be a specific instance, one I've just created.
I essentially want to be able to say: "Give me an instance of this class with all of its dependencies injected, but for a particular parameter, pass in this specific instance".
I've looked to see if PHP-DI (and a hand-full of other DI containers) support this kind of "override" for specific parameters, but so far I can't find anything.
What I want to know is:
Is there a DI container out there that can do this?
Is there an alternative approach which would leave the classes clean (I don't want to use annotations or anything else that'll add the container I use as a dependency)?
PHP-DI author here.
So there are two things, first I'll answer your question:
PHP-DI's container provides a make method that you can use like that:
$request = new Request($myParameters);
$controller = $container->make('RegistrationController', array(
'request' => $request
));
This make method, as you can see, is the same as get except it will always create a new instance (which is what you want here since you probably don't want to reuse an existing instance of the controller) and it will take the extra parameters you give it. That's the behavior of a factory, with the benefits of the container that will find the rest of the parameters you didn't provide.
So that's what I would use here. You could also do this:
$request = new Request($myParameters);
$container->set('Request', $request);
$controller = $container->get('RegistrationController');
But that's less clean because it will set the request in the container, which is bad (explained below).
Now the second thing is that a request object is not really a service, it's a "value object". A container should generally only contain service objects, i.e. objects that are stateless.
The reason for this is imagine you have several request in the same process (e.g. you do "sub-requests", or you have a worker process that handles several requests, etc...): your services would be all messed up because they would have the request injected and the request object might change.
Symfony did just that and realized it was a mistake. Since Symfony 2.4, they have deprecated having the Request in the container: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
Anyway, so what I suggest you to do is not to have the Request object in the container, but instead use the make method I showed you.
Or, even better, I would do that:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(UserRepo $user_repo)
{
$this->user_repo = $user_repo;
}
public function userListAction(Request $request)
{
// ...
}
}
// in the front controller
$controller = $container->make('RegistrationController');
// This is what the router should do:
$action = ... // e.g. 'userListAction'
$controller->$action(new Request($myParameters));
(this is what Symfony and other frameworks do by the way)
This is related to this question, but following that solution did not fix my issue. I also realize that Laravel's own documentation states that you should not mock the Request object, but I'm not sure how else to go about writing this test.
Here's a semblance of the code I want to test:
public function getThirdSegment()
{
return Request::segment(3);
}
Here's a test I currently have:
/**
* #test
*/
public function indexReturnsOk()
{
$this->prepareForTests();
$this->call('GET', '/api/v1/courses');
$this->assertResponseOk();
$this->assertResponseStatus(200);
}
This is failing because Request::segment(3) is returning null when running PHPUnit. I first tried to mock the Request object like this:
Request::shouldReceive('segment')->andReturn('courses');
But it still returns null. Then I tried this:
$request = m::mock('Illuminate\Http\Request');
$request->shouldReceive('segment')->andReturn('courses');
Input::swap($request);
And the segment method is still returning null. Is there any way to mock the return value of this method?
Update
This code is within a service provider's register method, but I don't think that's the cause of the issue. Hitting the site from a browser does what I would expect it to do, yet running PHPUnit doesn't seem to flesh out either the route or the URL, or anything having to do with the request itself.
Best answer here so far is I was doing it wrong. Service Providers run way before a controller is even loaded, and, when unit testing, Laravel's Illuminate\Foundation\Testing\TestCase loads the application (calling all service providers' both boot and register methods) during execution of the setUp method, way before any calls can be made out during the execution of any individual test.
I tried finding a solution by moving the logic down and got something to work, something along the lines of:
class MyTestClass extends TestCase
{
public function setUp()
{
// No call to parent::setUp()
// From: Illuminate\Foundation\Testing\TestCase
$this->app = $this->createApplication();
$this->client = $this->createClient();
// Not this one!
//$this->app->setRequestForConsoleEnvironment();
$this->app->boot();
// ...
}
public function testWhatever()
{
// Calls to this will now actually have a request object
$this->call('GET', '/api/v1/courses');
}
}
But that just can't be right, at least it doesn't feel so.
Instead, I figure it's probably best not to rely on anything in the Request object from within Service Providers. Instead, why not just inject an object that can do what I need it to do in the controller I want it to? No Service Provider necessary, and I can easily mock any object other than Request in Unit Tests. I should've believed the docs.
Update
Taking this answer to my own question a bit further, I believe my original mistake was that I was utilizing the Request object within a Service Provider. I think, on retrospection, that you should probably never use the Request object at all within a service provider, because providers get loaded for everything related to laravel (including artisan commands, which of course should have no request). My original code worked in the browser, but I probably would have noticed issues had I tried to run any artisan commands.
I came across this looking for a good way to handle a view composer that uses the request, and ultimately ended up going with constructor injection - because Laravel uses the Symfony Request class, it was really easy to "mock" a request with Request::create('http://myurl.com'), and pass it into an instance of my class for testing. Much better than trying to mess with stubbing methods on the request class, and doesn't rely on building up the whole Laravel application.
For clarity's sake, the view composer looks generally like this:
class SiteLayoutComposer extends BaseComposer {
function __construct(Request $request) {
$this->request = $request;
}
public function compose($view)
{
$templateName = preg_match('/siteurlexample/', $this->request->getHost()) ? 'site1' : 'site2';
$view->with('siteLayout', "layouts.sites.{$templateName}");
}
}
And the test:
class SiteLayoutComposerTest extends TestCase {
public function testWillSetTheSiteLayoutToSite1()
{
$request = Request::create('http://www.siteurlexample.com');
$view = View::make('home');
$composer = new SiteLayoutComposer($request);
$composer->compose($view);
$this->assertEquals('layouts.sites.site1', $view->siteLayout);
}
}
I am working on setting up a testing suite for a PHP Propel project
using Phactory, and PHPUnit. I am currently trying to unit test a
function that makes an external request, and I want to stub in a mock
response for that request.
Here's a snippet of the class I am trying to test:
class Endpoint {
...
public function parseThirdPartyResponse() {
$response = $this->fetchUrl("www.example.com/api.xml");
// do stuff and return
...
}
public function fetchUrl($url) {
return file_get_contents($url);
}
...
And here's the test function I am trying to write.
// my factory, defined in a seperate file
Phactory::define('endpoint', array('identifier' => 'endpoint_$n');
// a test case in my endpoint_test file
public function testParseThirdPartyResponse() {
$phEndpoint = Phactory::create('endpoint', $options);
$endpoint = new EndpointQuery()::create()->findPK($phEndpoint->id);
$stub = $this->getMock('Endpoint');
$xml = "...<target>test_target</target>..."; // sample response from third party api
$stub->expects($this->any())
->method('fetchUrl')
->will($this->returnValue($xml));
$result = $endpoint->parseThirdPartyResponse();
$this->assertEquals('test_target', $result);
}
I can see now, after I tried my test code, that I am creating a mock object
with getMock, and then never using it. So the function fetchUrl
actually executes, which I do not want. But I still want to be able to use
the Phactory created endpoint object, since it has all the right fields
populated from my factory definition.
Is there a way for me to stub a method on an existing object? So I could stub
fetch_url on the $endpoint Endpoint object I just created?
Or am I going about this all wrong; is there a better way for me to unit test
my functions that rely on external web requests?
I did read the PHPUnit documentation regarding "Stubbing and Mocking Web Services", but their sample code for doing so is 40 lines long, not including having to define your own wsdl. I'm hard pressed to believe that's the most convenient way for me to handle this, unless the good people of SO feel strongly otherwise.
Greatly appreciate any help, I've been hung up on this all day. Thanks!!
From a testing perspective, your code has two problems:
The url is hardcoded, leaving you no way of altering it for development, testing or production
The Endpoint knows about how to retrieve data. From your code I cannot say what the endpoint really does, but if it's not a low level "Just get me Data" object, it should not know about how to retrieve the data.
With your code like this, there is no good way to test your code. You could work with Reflections, changing your code and so on. The problem with this approach is that you don't test your actual object but some reflection which got change to work with the test.
If you want to write "good" tests, your endpoint should look something like this:
class Endpoint {
private $dataParser;
private $endpointUrl;
public function __construct($dataParser, $endpointUrl) {
$this->dataPartser = $dataParser;
$this->endpointUrl = $endpointUrl;
}
public function parseThirdPartyResponse() {
$response = $this->dataPartser->fetchUrl($this->endpointUrl);
// ...
}
}
Now you could inject a Mock of the DataParser which returns some default response depending on what you want to test.
The next question might be: How do I test the DataParser? Mostly, you don't. If it is just a wrapper around php standard functions, you don't need to. Your DataParser should really be very low level, looking like this:
class DataParser {
public function fetchUrl($url) {
return file_get_contents($url);
}
}
If you need or want to test it, you could create a Webservice which lives within your tests and acts as a "Mock", always returning preconfigured data. You could then call this mock url instead of the real one and evaluate the return.
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.