Laravel 5 isolated controller testing - unit-testing

I'm trying to unit test my controllers in Laravel 5, but have serious issues wrapping my head around it. It seems as I have to trade the great short-hand functions and static classes for dependency injected equivalents if I actually want to do isolated unit testing.
First off, what I see in the documentation as "Unit testing", is not unit testing to me. It seems more like functional testing. I can not test a controller function isolated, as I will have to go through the entire framework, and will need to actually seed my database if I have any code interacting with my database.
So, in turn, I want to test my controllers isolated of the framework. This is however proving to be quite difficult.
Let's look at this example function (I've kept out some parts of this function for the sake of the question):
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath());
}
}
Now, the problem arises in the final lines. Sure, I can mock the Request instance that's sent to the function, that's no issue. But how am I going to mock the Auth class, or the redirect function? I need to rewrite my class/function with dependency injection like this:
private $auth;
private $redirector;
public function __construct(Guard $auth, \Illuminate\Routing\Redirector $redirector)
{
$this->auth = $auth;
$this->redirector = $redirector;
}
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
return $this->redirector->intended($this->redirectPath());
}
}
And I end up with a convoluted unit test, full of mocks:
public function testPostLoginWithCorrectCredentials()
{
$guardMock = \Mockery::mock('\Illuminate\Contracts\Auth\Guard', function($mock){
$mock->shouldReceive('attempt')->with(['email' => 'test', 'password' => 'test'], false)->andReturn(true);
});
$redirectorMock = \Mockery::mock('\Illuminate\Routing\Redirector', function($mock){
$mock->shouldReceive('intended')->andReturn('/somePath');
});
$requestMock = \Mockery::mock('\Illuminate\Http\Request', function($mock){
$mock->shouldReceive('only')->with('email', 'password')->andReturn(['email' => 'test', 'password' => 'test']);
$mock->shouldReceive('has')->with('remember')->andReturn(false);
});
$object = new AuthController($guardMock, $redirectorMock);
$this->assertEquals('/somePath', $object->postLogin($requestMock));
}
Now, if I had any more complex logic that would, for example, use a model, I'd have to dependency inject that as well, and mock it in my class.
To me, it seems like either, Laravel isn't providing what I want it to do, or my testing logic is flawed. Is there any way I can test my controller functions without getting out-of-control testing functions and/or having to depedency inject standard Laravel classes, available to me anyways in my controller?

You shouldnt be trying to unit test controllers. They are designed to be functional tested via calling them through the HTTP protocol. This is how controllers are designed, and Laravel provides a number of framework assertions you can include in your tests to ensure they work as expected.
But if you want to unit test the application code contained within the controller, then you should actually consider using Commands.
Using Commands allows you to extracte the application logic from your controller into a class. You can then unit test the class/command to ensure you get the results expected.
You can then simply call the command from the controller.
In fact the Laravel documentation tells you this:
We could put all of this logic inside a controller method; however, this has several disadvantages... it is more difficult to unit-test the command as we must also generate a stub HTTP request and make a full request to the application to test the purchase podcast logic.

Related

Checking middleware is called from http call

How would one test that a piece of custom middleware is actually called from a standard HTTP event?
ie. The middleware is called from:
MyController.js
router.get('/some/endpoint', [myMiddleware()], (req, res, next) => {
// Code to do whatever here
});
The middleware itself can be defined as:
MyMiddleware.js
module.exports = () => {
// Middleware code in here
}
My quest is to check that the middleware is called once from my unit test, but I cannot find documentation around this.
MyTest.test.js
it('Should return whatever from GET call', () => {
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {res.body.should.deep.equal(bodyValue)});
// How would I place code in here to check that MyMiddleware is called?
// ie. sinon.assert.calledOnce(MyMiddleware)
});
I have thought about using Sinon's spy, but I can't think of how to hook into the middleware... My attempt was this:
const mwSpy = sinon.spy(require('path to middleware file'));
sinon.assert(calledOnce(mwSpy));
The usual way of going about this is splitting this into two tests, an integration test and a unit test.
Will the middleware I specified in the router.get call end up being called when someone hits this endpoint?
Does my middleware do the right thing?
The first part is basically testing that the Express API is doing what the documentation says. That's not what unit tests are for (this was tagged unit-testing), but since you are already using HTTP requests to test the endpoint, I guess that's not what you are after anyway: you are basically creating verification tests for your system.
You could still test the Express routing without HTTP, though, as I detail in the answer to this question, concerning how to test the router programmatically (faster tests, no http), but less just stick to what you have.
So the basic question is: "My quest is to check that the middleware is called once from my unit test". You don't seem to concern yourself with whether the middleware is doing the right thing or not, just that it's called, which calls for the question on whether we should test the middleware or the layer using the middleware.
In both cases, you need to find a way of injecting a test spy. Either you write a small utility method that will inject that spy: function setMiddleware(module){ middleware = module; } or you use some tooling like proxyquire. See this tutorial on Sinon's homepage for background.
I would just do this (in the test code):
it('Should return whatever from GET call', () => {
var middlewareFake = sinon.fake();
// I am assuming it's the actual app object you are referencing below in the request(app) line
var app = proxyquire('../app/index.js', { './my-middleware': middlewareFake });
//
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {
res.body.should.deep.equal(bodyValue)
expect(middlewareFake).was.called;
});
});

Grails Unit Testing Issues

I am having issues while testing my grails controllers, as it depends on one service which seems not to be injected. I tried several ways (for ex. Extending classess like grailsunitestcase, specification) but I keep getting errors. The thing is that that service variable is null and I cant test my controller index method (which calls a render view) due to the exception...
I really need to know how to do this but I don't have a clue where to start...
Unit tests are just that. There is no grails 'environment' surrounding your controller. If the controller makes use of a service which is normally injected, you have to mock that service yourself.
#TestFor(SomeController)
#Mock([SomeService])
class SomeControllerSpec extends Specification
def "test some method"() {
given:
def mockService = mockFor(SomeService)
mockService.demand.someServiceMethod() { ->
return something
}
controller.someService = mockService.createMock()
when:
controller.someControllerMethod()
then:
// whatever checks are appropriate
}
}

How can globally I mock/replace components in integration tests?

I have a component "nested" which makes a web request, and I'm using this component from another component "parent".
I'm trying to write some integration tests for "parent", but they are failing as the "nested" component's web requests are failing.
Instead of mocking out the requests, I was just hoping to mock some of the "nested" functionality to prevent the web request. This is easily achievable with reopen, but of course this will cause the tests for "nested" to fail.
Does anyone know if there is a way that I can either stub pieces of "nested", or maybe use the registry to replace "nested" with an extended class?
To swap out a component, just for the purpose of a single test module, simply register a custom component to replace the original (only for the scope of the test module):
moduleForComponent('component-under-test', 'description', {
integration: true,
beforeEach() {
this.container.registry.register('component:nested-component', NestedComponent.extend({
modifiedFunction() {
}
}));
}
});

How do I write test(s) for this in Symfony?

I'm about to delve into test in the PHP world and I have some questions. I have a controller which handles a loan application. The bulk of the work is then delegated to a ProcessLoanApplication class.
ApplyController
class ApplyController extends Controller
{
public function indexAction(Request $request)
{
$form = $this->createForm(new LoanApplication());
if($request->getMethod() == 'POST') {
$form->bind($request);
if($form->isValid()) {
$session = $this->getRequest()->getSession();
$loan_app_processor = new Tasks\ProcessLoanApplication($form, $session);
$loan_app_processor->process();
return $this->redirect($this->generateUrl('apply_thanks'));
}
}
Tasks\ProcessLoanApplication
class ProcessLoanApplication
{
private $_QuickBaseModels;
private $_session;
private $_app; // submitted form data
private $_existingApp = false; // holds existing application in QB, if it exists
public function __construct(Form $form, Session $session)
{
$this->_app = $form->getNormData();
$this->_session = $session;
// save the form data to a session
$session->set('application', $this->_app);
// create the quickbase table models objects
$this->_QuickBaseModels['GenFnHome\Application'] = new GenFnHome\Application();
$this->_QuickBaseModels['GenFnHome\SSN'] = new GenFnHome\SSN();
}
public function process()
{
$this->_existingApp = $this->_getExistingApplication();
$app_status = $this->_existingApp[GenFnHome\SSN::LogInApplicationStatus];
if(!$this->_existingApp || ($this->_existingApp && ($app_status !== 'PENDING' && $app_status !== 'OPEN' && $app_status !== 'EXPIRED')))
return $this->_saveNewLoanApplication();
if($app_status == 'EXPIRED') $this->_reOpenApplication();
}
There's a lot going on here, so I will outline it first:
User makes a requests for the application
Application form is validated
If valid, process loan application
Check if the user already has an app, if so - do X, if not Y
The application is persisted in an 'online database' (QuickBase) that my application communicates with via XML over HTTP (in other words, there is no real db)
My questions to the community:
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
Anything else I should know?
Thanks!
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
I recommend to test every class you build. If you are using Test Driven Development, the test declares what you are building, no test no code.
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Every Unit test should be isolated and should only test the Class that you are testing. You should use Mock object (use the PHPunit mock library or other 3th party libraries as Mockery) if one object dependences on another object.
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
You can use the Symfony's WebTestCase that provides easy methods to imitate a browser request, learn more about that in the documentation. We call that Functional Testing.
This is usually the stage after Unit Testing. In Unit Testing you will test each individual class (it's a good practice to unit test your controller) and after that you write your Functional Tests which combines everything and tests if it works like expected.
for Controllers you should use functional tests (http://symfony.com/doc/2.0/book/testing.html#functional-tests). With them you can emulate browser and user's actions like submiting form and checking validation, database changes, http status codes and so on.
You should not forget to unit test ProcessLoanApplication.
I dont realy know why you pass form object to ProcessLoanApplication anyway. You should pass entity - it has normdata already.

Unit testing Zend controllers with mock models and services

I've read a lot of Zend controller testing tutorials but I can't find one that explains how to test a controller that uses models and mocking those models.
I have the following controller action:-
function indexAction(){
// Get the cache used by the application
$cache = $this->getCache();
// Get the index service client and model
$indexServiceClient = new IndexServiceClient($this->getConfig());
$indexModel = $this->_helper->ModelLoader->load('admin_indexmodel', $cache);
$indexModel->setIndexServiceClient($indexServiceClient);
// Load all the indexes
$indexes = $indexModel->loadIndexes();
$this->view->assign('indexes', $indexes);
}
At the moment I have a very basic test case:-
public function testIndexActionRoute() {
$this->dispatch( '/admin/index' );
$this->assertModule('admin', 'Incorrect module used');
$this->assertController('index', 'Incorrect controller used');
$this->assertAction('index', 'Incorrect action used');
}
This test works but it's calling the real models and services, which sometimes means it times out and fails on the test environment. In order to properly unit test just the controller I need to have mocks and expectations for IndexServiceClient and IndexModel - how is this done?
Well, since not many replies I am seeing here, I'll try to add my 2cents(potentially arguable).
Answer written below is my IHMO and very subjective(and I think not very useful, but here we go anyway)
I think controllers are not a good fit unit testing. Your business logic layer, models etc. is what is unitestable.
Controllers are connected with UI and bring system together so to speak - hence to me they are a better fit for integration and UI testing - something that packages such as Selenium are used for.
To my mind testing should be easy enough to implement such that total effort for testing implementation is adequate to the returns of it. Wiring up all dependencies for a controller seems to me(with my limited knowledge of course) a bit too much of a task.
The other way to think about it is - what is actually going on in your controllers. Again IHMO it supposed to be primarily a glue level between your business logic and your UI. If you're putting a lot of business logic into controller it will have an adverse effect(for instance it won't be easily unitestable..).
This is all sort of theory of course. Hopefully someone can provide a better answer and actually show how to easily wire up a controller for unit tests!
One possible solution that a colleague put forward is to use a Zend Controller Action Helper to inject mock dependencies. This should work in theory but I've yet to extensively test this method
Here is an example of doing it this way.
class Mock_IndexModel_Helper extends Zend_Controller_Action_Helper_Abstract {
private $model;
public function __construct($model) {
$this->model = $model;
}
/**
* Init hook
*/
public function init() {
$this->getActionController()->setIndexModel( $this->model );
}
}
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase {
public $bootstrap = BOOTSTRAP;
public function setUp(){
parent::setUp();
}
/**
* Test the correct module/controller/action are used
*/
public function testIndexAction() {
$mockIndexModel = $this->getMock("IndexModel");
Zend_Controller_Action_HelperBroker::addHelper(new Mock_IndexModel_Helper($mockIndexModel));
$this->dispatch( '/admin/index' );
$this->assertModule('admin', 'Incorrect module used');
$this->assertController('index', 'Incorrect controller used');
$this->assertAction('index', 'Incorrect action used');
}
}