Reputation: 13
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');
}
}
Upvotes: 1
Views: 2598
Reputation: 13
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.
Upvotes: 0
Reputation: 1814
It actually a a bug on how exception is handled during running test case, this however has been fixed, just run composer update.
Upvotes: 1