LavaWings
LavaWings

Reputation: 386

Mockery false positive in Laravel controller test

I'm trying to learn how to use Mockery with Laravel 5. I've based my efforts mostly on Way's book (Laravel Testing Decoded) and other tutorials, which say integration [with PHPUnit] only requires the tearDown() method. So I've included that. The problem is that it doesn't seem to be resetting things between tests. My test class contents look essentially like this:

public function __construct()
{
    $this->mock = Mockery::mock('Class\To\Mock');
}

public function tearDown()
{
    Mockery::close();
}

public function test_RedirectWithoutAuthentication()
{
    // Act
    $this->call('GET', '/path/1');

    // Assert
    $this->assertRedirectedTo('/auth/login');
}

public function test_X()
{
    // Arrange
    $this->mock->shouldReceive('MockedClassMethod')->once();

    // Act
    $this->call('GET', '/path/1');
}

The first test works and the Auth middleware kicks the user to the login page. In the interest of TDD, I've written the second test before the MockedClassMethod is actually written. So to my way of thinking, it should fail spectacularly. But it doesn't. It passes!

If I change the order of the tests, it "works" (unwritten fails, auth passes) which leads me to believe that it's not really an order problem, but something to do with one test not getting cleaned up before the next.

Any insights will save my remaining hair from being pulled out. :-)

Upvotes: 2

Views: 314

Answers (2)

LavaWings
LavaWings

Reputation: 386

I had tried using the setUp() method as described by Matteo, and it caused the tests not to run at all. So I abandoned that course thinking I was way off. But Matteo's suggestion turned me back to it.

A little digging into why it caused the tests to fail showed that the $app object was never getting created. Hmm... So then it dawned on me that the setUp() method was overriding some important stuff. So to fix it, all that was needed was to call the parent method first! Like so:

public function setUp()
{
    parent::setUp();
    $this->mock = Mockery::mock('Class\To\Mock');
}

From that point, the setUp() and tearDown() worked as expected.

I'm still not sure why all the examples I seem to find show the mock being created in the constructor. Maybe I'm still not getting something, but at least it's working... for now. :-)

Upvotes: 2

Matteo
Matteo

Reputation: 39370

In accordion with the PHPUnit doc:

The setUp() and tearDown() template methods are run once for each test method (and on fresh instances) of the test case class.

The constructor is called only the first time so the tearDown is called before the second test executed of the class and the effect is that the mockery instance was closed.

In order to solve your problem you can use the setup method for init the mocked object. So replace the class constructor of the test class with the setup method as follow:

Try to use this:

protected function setUp() 
{
    $this->mock = Mockery::mock('Class\To\Mock');
}

instead of:

public function __construct()
{
    $this->mock = Mockery::mock('Class\To\Mock');
}

Hope this help

Upvotes: 2

Related Questions