mockery->shouldReceive() passing when it shouldnt?
Asked Answered
T

3

8

I am learning unit testing in laravel using phpunit and mockery. I am currently trying to test UsersController::store().

I am mocking User Model and using it to test the index method and that seems to work. When I take out $this->user->all() the test fails and when its in it passes.

When testing the store method though I am using the mock to test that the user model receives validate() once. The store method is empty but the test passes. I have left out the irrelevant pieces of the class for brevities sake

<?php

class UsersController extends BaseController {

    public function __construct(User $user)
    {
        $this->user = $user;
    }
    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        $users = $this->user->all();

        return View::make('users.index')
        ->with('users', $users);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        return View::make('users.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store()
    {
        //
    }

}

UserControllerTest.php

<?php
    use Mockery as m;
class UserControllerTest extends TestCase {

    public function __construct()
    {
        $this->mock = m::mock('BaseModel', 'User');
    }

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

    public function testIndex()
    {
        $this->mock
            ->shouldReceive('all')
            ->once()
            ->andReturn('All Users');
        $this->app->instance('User', $this->mock);
        $this->call('GET', 'users');
        $this->assertViewHas('users', 'All Users');
    }

    public function testCreate()
    {
        View::shouldReceive('make')->once();
        $this->call('GET', 'users/create');
        $this->assertResponseOk();
    }

    public function testStore()
    {

        $this->mock
            ->shouldReceive('validate')
            ->once()
            ->andReturn(m::mock(['passes' => 'true']));
        $this->app->instance('User', $this->mock);
        $this->call('POST', 'users');
    }


}
Tetramethyldiarsine answered 11/12, 2013 at 22:13 Comment(0)
R
1

You shouldn't overwrite the constructor of PHPUnit_Framework_TestCase, use setUp for initialization purposes. See also my answer on #15051271 and also #17504870

Rivalee answered 11/12, 2013 at 22:34 Comment(2)
Thank you I think this did the trick. At least testStore is failing now. However, my tests extend testcase which extend phpunits testcase. testcase has setUp(){parent::setUp(); $this->prepareForTests();} so using setUp in each individual test class would overwrite this am I right? Is there another way? I just am creating a mock object in each test function right now to make it work.Tetramethyldiarsine
The setUp method in your class also need to call the parent::setUp(). That should be the way to go.Rivalee
G
17

Mockery is by default a stubbing library, not a mocking one (which is confusing because of its name).

That means that ->shouldReceive(...) by default is "zero or more times". When using ->once(), you say it should be called zero or one time, but not more. This means it'll always pass.

When you want to assert that it is called once, you can use ->atLeast()->times(1) (one or more times) or ->times(1) (exactly one time)

Galloot answered 11/12, 2013 at 22:21 Comment(2)
Thank you for the quick reply. Why then does testIndex fail if I remove the call to all in index()? I just swapped out once() for times(1) in both tests and still got the same results both pass. when I remove the call to all() in index() testIndex fails. Incidently in testindex ragardless of weather I use once() or times(1) the invalidcountexception from mockery says all() should be called exactly 1 times but called 0times.Tetramethyldiarsine
"->shouldReceive(...)" equals "zero or more times" only by default, it doesn't affect other methods. "->once()" states that it should be called exactly one time, if you don't call it, it fails.Duplicator
R
6

To complete Wounter's answer, you must call Mockery::close().

This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for your expectations.

This answer helped me understand this concept.

Raybourne answered 16/2, 2016 at 16:54 Comment(0)
R
1

You shouldn't overwrite the constructor of PHPUnit_Framework_TestCase, use setUp for initialization purposes. See also my answer on #15051271 and also #17504870

Rivalee answered 11/12, 2013 at 22:34 Comment(2)
Thank you I think this did the trick. At least testStore is failing now. However, my tests extend testcase which extend phpunits testcase. testcase has setUp(){parent::setUp(); $this->prepareForTests();} so using setUp in each individual test class would overwrite this am I right? Is there another way? I just am creating a mock object in each test function right now to make it work.Tetramethyldiarsine
The setUp method in your class also need to call the parent::setUp(). That should be the way to go.Rivalee

© 2022 - 2024 — McMap. All rights reserved.