1

I'm new to Laravel and the concept of the IoC. I was following the great tutorials over a Nettuts (http://net.tutsplus.com/tutorials/php/testing-laravel-controllers/) and was able to successful test my controller. However I wanted to isolate the controller by mocking the database. As soon as I attempted to inject my mocked object into the IoC I get the following error:

Cannot modify header information - headers already sent by (output started at /Users/STRATTON/Dev/SafeHaven/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php:172)

The line it's referring to outputs PHPUnit's buffer using the 'print' construct. Something is causing output to be sent before headers are set but I can't track down the problem.

I'm able to run all my tests successfully when the controller calls the real model and makes the database call. At the same time I'm able to mock the object successfully and exercise mock without error. But as soon as I attempt to inject the mocked object using App::instance() the error appears.

I've also tested this with PHPUnit's mocks and get the same results. Am I mocking the object properly? Do I have a problem with namespacing? Am I missing something that's outputting content?

Controller:

<?php namespace App\Controllers;

use App\Models\Repositories\ArticleRepositoryInterface;

class HomeController extends BaseController {

    protected $articles;

    public function __construct(ArticleRepositoryInterface $articles) 
    {
        $this->articles = $articles;
    }

    public function index()
    {
        $articles = $this->articles->recent();
        return \View::make('home.index')
            ->with('articles', $articles);
    }

}

TestCase

<?php namespace Tests\Controllers;

class HomeControllerTest extends \TestCase {

    public function testIndex()
    {
        $mocked = \Mockery::mock('App\\Models\\Repositories\\ArticleRepositoryInterface');
        $mocked->shouldReceive('recent')->once()->andReturn('foo');
        \App::instance('App\\Models\\Repositories\\ArticleRepositoryInterface', $mocked);
        //$mocked->recent();

        $this->call('GET', '/');
        $this->assertResponseOk();
        $this->assertViewHas('articles');
    }

}
1
  • Even though the answer below has solved the above problem it does bring up another question. How do you test your controller in isolation from your views? Commented May 18, 2013 at 6:35

2 Answers 2

1

It actually a a bug on how exception is handled during running test case, this however has been fixed, just run composer update.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank You! PHPUnit now outputs the Exception and I can actually tracked down the problem! I had actually run composer update before asking the question, so this must be a really fresh fix. Which library was causing this issue?
This commit actually solve it github.com/laravel/framework/commit/…, there also a discussing resulting to the commit at github.com/laravel/framework/issues/1332
0

Answering my own question - The reason for the error is that some part of the code is causing a PHP error or an exception to be thrown.

In this case, the problem was an Exception thrown from the View. The View was expecting that the value returned by the method recent() was an Eloquent Collection (Illuminate\Database\Eloquent\Collection), or at least something that the View could iterate over.

THe HomeControllerTest::TestIndex method was mocking the object and when recent() was called it returned 'foo'. There's no way for the View to iterate over a string so it throws an exception. The two solutions are below, the later allowing the ability to test that the view received the correct object type.

$mocked->shouldReceive('recent')
    ->once()
    ->andReturn([]);

If you're having a similar issue, examine all the code being tested and make sure you're tests actually fulfill all the requirements/dependancies... or use TDD... something I should have done from the start and avoided this issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.