Setting attributes on models while testing - php

I am trying to test a controller and am mocking the models. All seems to go well until the view is loaded, it's unable to retrieve properties on that view that are supposed to be loaded via relationships.
I've tried setting those properties using andSet() on the mocked object, but then that gives me an error getAttribute() does not exist on this mocked object..
Here's my controller method.
public function __construct(ApplicationRepositoryInterface $application)
{
$this->beforeFilter('consumer_application');
$this->application = $application;
}
public function edit($application_id)
{
$application = $this->application->find($application_id);
$is_consumer = Auth::user()->isAdmin() ? 'false' : 'true';
return View::make('application.edit')
->with('application', $application)
->with('is_consumer', $is_consumer)
->with('consumer', $application->consumer);
}
And my test...
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock($this->app->make('ApplicationRepositoryInterface'));
}
public function testEdit()
{
$this->app->instance('ApplicationRepositoryInterface', $this->mock);
$this->mock
->shouldReceive('find')
->once()
->andReturn(Mockery::mock('Application'))
->andSet('consumer', Mockery::mock('Consumer'));
Auth::shouldReceive('user')
->once()
->andReturn(Mockery::mock(array('isAdmin' => 'true')));
$application_id = Application::first()->id;
$this->call('GET', 'consumer/application/'.$application_id.'/edit');
$this->assertResponseOk();
$this->assertViewHas('application');
$this->assertViewHas('is_consumer');
$this->assertViewHas('consumer');
}
The furthest I have gotten is removing the the andSet() portion which takes care of the getAttribute() does not exist on this mock object but then it tells me consumer is undefined when the view is loaded and still fails.

You should change:
Auth::shouldReceive('user')
->once()
->andReturn(Mockery::mock(array('isAdmin' => 'true')));
To this:
Auth::shouldReceive('user->isAdmin')
->once()
->andReturn('true');

Related

Laravel 5.1 Mockery expectation ignored while testing controller

I have a Controller depending on a UserManager. This is the controller constructor:
public function __construct(UserManager $manager) {
$this->manager = $manager;
}
This is the test code.
public function test_something() {
$this->withoutMiddleware();
// Setup Input.
$user = ['email' => 'foo#bar.baz', 'password' => 'pass', 'accessLevel' => 'admin'];
// Setup expectations.
$mock = \Mockery::mock("Users\UserManager")->shouldReceive('foo');
// Bind to container... not sure whether this is needed.
$this->app->instance("Users\UserManager", $mock);
// Call action.
$this->call('POST', 'api/v1/temp/users', ['user' => $user]);
}
I set the expectation on the foo method, which doesn't exists and therefore is not invoked anywhere, however my test won't fail.
Why?
You need to specify how many times the foo method should be called:
->shouldReceive('foo')->once();
Make sure that you also have a tearDown method where you reset Mockery or it won't work:
public function tearDown()
{
Mockery::close();
}

Trying to swap a model in my controller test with IoC Container, but object not swapper

I'm trying to use IoC Container to swap out my Question model when testing. Although I've created a mock model, and using App::instance() to attempt to swap the dependency during my test, I can see from var_dump that it isn't working. What is wrong with my code?
<?php
class QuestionsControllerTest extends TestCase {
protected $mock;
public function __construct()
{
// This is how Net tuts tutorial instructed, but
// I got Eloquent not found errors
// $this->mock = Mockery::mock('Eloquent', 'Question');
// so I tried this instead, and it created the mock
$this->mock = Mockery::mock('App\Question');
}
public function tearDown()
{
Mockery::close();
}
public function testQuestionIndex()
{
// var_dump(get_class($this->mock)); exit; // outputs: Mockery_0_App_Question
// var_dump(get_class($this->app)); exit; // outputs: Illuminate\Foundation\Application
$this->mock
->shouldReceive('latest')
->once()
->andReturnSelf();
$this->mock
->shouldReceive('get') //EDIT: should be get
->once()
->andReturn('foo');
$this->app->instance('App\Question', $this->mock);
// dispatch route
$response = $this->call('GET', 'questions');
$this->assertEquals(200, $response->getStatusCode());
}
}
So far so good? Below is my QuestionsController:
class QuestionsController extends Controller {
protected $question;
public function index(Question $question)
{
// var_dump(get_class($question)); exit; // Outputs App\Question when testing too
$questions = $question
->latest()
->get();
return view('questions.index', compact('questions'));
}
...
So, without the object being swapped, it doesn't register anyway call to the methods:
Mockery\Exception\InvalidCountException: Method latest() from Mockery_0_App_Question should be called
exactly 1 times but called 0 times.
By the way, I've installed Mockery ~0.9, Laravel 5.0, and PHPUnit ~4.0. Would really really appreciate any help on this.
I think you need to specify the full namespace when using instance(). Laravel otherwise will assume the model is in the global namespace ('\Question').
This should work:
$this->app->instance('App\Question', $this->mock);
Now regarding the other problem, your mock. Since your view wants a collection why not just give it one? And if you don't want to test the view you can simply instantiate an empty collection and return that:
$this->mock
->shouldReceive('latest')
->once()
->andReturnSelf();
$this->mock
->shouldReceive('get')
->once()
->andReturn(new Illuminate\Database\Eloquent\Collection);
If you want you can also check if the view has correctly received the variable with:
$response = $this->call('GET', 'questions');
$this->assertViewHas('questions');
You are getting this error as you didn't define your mock completely.
you told your mock that it should expect latest to be called once, but you did not specify what latest should return. on the next line in your controller you call get.
Try the following code
$this->mock
->shouldReceive('latest')
->once()
->andReturnSelf();
$this->mock
->shouldReceive('get') //EDIT: should be get
->once()
->andReturn('foo');
$this->app->instance('Question', $this->mock);
Very good article about testing controllers in larvel http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456

How create return values when mocking Eloquent on Laravel

I am working on a project on Laravel, and I have developed my own version of User. I know that Laravel comes with it is own implementation, but just for the sake of the argument, let's forget that.
I am trying to learn how to test in Laravel, and I encountered the following problem:
Part of the controller
<?php
class UsersController extends BaseController {
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function login() {
// Input, Validation, blah, blah blah
....
// Find user
$users = $this->user->where('email', '=', $email);
if ($users->count() == 0) {
// Do something
} else {
// Do something else different
}
// Lot more stuff...
}
}
Now comes testing. As you see I put the dependency of Eloquent through the constructor so I can mock it. The test are as follow:
<?php
class UsertTest extends TestCase {
public function __construct() {
$this->userMock = Mockery::mock('Eloquent', 'User');
}
public function tearDown() {
Mockery::close();
}
public function testLogin() {
// Unimportant
$data = ...
$this->userMock
->shouldReceive('where')
->once()
->andReturn('foo');
$this->app->instance('User', $this->userMock);
// Send the data
$this->post('/login', $data);
When I run the tests, I get this error
PHP Fatal error: Call to a member function count() on a non-object
So it seems that the mock is working, in the sense that the Database is not being hit, but now I am with the problem that I need to mock as well the value returned by the call.
How should I go about it?

Laravel mock with Mockery Eloquent models

I'm developing a PHP (5.4.25) application with laravel(4.2) framework. I'd like test my UserController with Mockery, so I've fit my UserController in this way:
class UsersController extends \BaseController {
protected $user;
public function __construct(User $user) {
$this->user = $user;
$this->beforeFilter('csrf', array('on'=>'post'));
}
public function store() {
$validator = Validator::make(Input::all(), User::$rules);
if ( $validator->passes() ) {
$this->user->username = Input::get('username');
$this->user->password = Hash::make(Input::get('password'));
$this->user->first_name = Input::get('first_name');
$this->user->last_name = Input::get('last_name');
$this->user->email = Input::get('email');
$this->user->save();
return true;
} else {
return false;
}
}
I want mock Eloquent User model so i develop my UsersControllerTest so:
class UsersControllerTest extends TestCase {
private $mock;
public function __construct() {}
public function setUp() {
parent::setUp();
$this->createApplication();
}
public function tearDown() {
parent::tearDown();
Mockery::close();
}
public function testStore() {
$this->mock = Mockery::mock('Eloquent','User[save]');
$this->mock
->shouldReceive('save')
->once()
->andReturn('true');
$this->app->instance('User', $this->mock);
$data['username'] = 'qwerety';
$data['first_name'] = 'asd';
$data['last_name'] = 'asd123';
$data['email'] = 'test#gmail.com';
$data['password'] = 'password';
$data['password_confirmation'] = 'password';
$response = $this->call('POST', 'users', $data);
var_dump($response->getContent());
}
}
When I run my test it returns me this error:
Mockery\Exception\InvalidCountException : Method save() from Mockery_0_User should be called
exactly 1 times but called 0 times.
Why? What's wrong?
EDIT: I found the problem - If I don't use mock object all works fine and the controller create a new user in the DB, but when I use mock the Input:all() method returns empty array.
--
Thanks
i had this same issue when i started testing.....
the thing there is that, in your userscontroller store method you are actually saving the user to the database and base on your code it might work just fine but you are surprise that it is actually failing the test.
Now think of it this way, you mock the user, and you told your test that when ever i call User model, please give me the mock version of my user, like you did in your code line below
$this->app->instance('User', $this->mock);
Now the problem is that you need to also call the mock version of save() method from the through the mock version of User like so:
$this->mock->save();
Consider the following Example:
public function testStore()
{
$input = ['name', 'Canaan'];
$this->mock
->shouldReceive('create')
->once()->with($input);
$this->app->instance('User', $this->mock);
$this->mock->create($input);
$this->call('POST', 'users', $input);
}
i hope it helps you.
The way you are testing this, in the controller constructor is passed an instance of the real Eloquent model, not the mock. If you want to test the facades (as clearly stated in the documentation) you should call the shouldReceive() method directly on the facade to have it mocked.
But, since you are redefining the $this->user variable in the store() method of the controller, it will not be called, unless you remove the hardcoded instantiation and use the injected $user.
Edit: i overlooke the $this->app->instance('User', $this->mock); line. So, the problem may be due the fact that in the store method you are getting a new class instance directly, and not via the Laravel IoC container. instead of $this->user = new User; in your store method, you should get it via App::make('User');
The problem was in $this->createApplication();.
I have commented that line and Input::all() works fine with all input parameters!

testing php laravel controller shouldReceive arguments

I have a laravel model which uses ardent/eloquent. I am trying to set up tests for the controller in particular, storing a new model that uses the ardent model.
The method works in the app but I'm having trouble with my tests
I'm having problems working out how to mock the calls this method makes.
My controllers set up and the method in question is this one:
use golfmanager\service\creator\TicketCreatorInterface;
//controller manages the ticket books
class BooksController extends BaseController {
/**
* Book Repository
*
* #var Book
*/
protected $book;
protected $ticket;
public function __construct(Book $book, TicketCreatorInterface $ticket)
{
$this->book = $book;
$this->ticket = $ticket;
}
public function store()
{
$input = Input::all();
$result = $this->book->save();
if ($result) {
//if book created then create tickets
$this->ticket->createTicket($input, $this->book);
return Redirect::route('books.index');
}
return Redirect::route('books.create')
->withInput()
->withArdentErrors()
->with('message', 'There were validation errors.');
}
And the methods used by the interface (TicketCreator):
public function createTicket($input, $book) {
//dd($input);
$counter = $input['start_number'];
while($counter <= $input['end_number']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
My test is as follows:
use Mockery as m;
use Way\Tests\Factory;
class BooksTest extends TestCase {
public function __construct()
{
$this->mock = m::mock('Ardent', 'Book');
$this->collection = m::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();
}
public function setUp()
{
parent::setUp();
$this->attributes = Factory::book(['id' => 1, 'assigned_date'=> '20/11/2013']);
$this->app->instance('Book', $this->mock);
}
public function testStore()
{
Input::replace($input = ['start_number'=>1000, 'end_number'=>1010, 'assigned_date'=>'20/11/2013']);
$this->mock->shouldReceive('save')->once()->andReturn(true);
$this->ticket->shouldReceive('createTicket')->once()->with($input, $this->mock)->andReturn(true);
//with($input);
//$this->validate(true);
$this->call('POST', 'books');
$this->assertRedirectedToRoute('books.index');
}
Currently I get an error:
No matching handler found for Book::save()
Is this being thrown because the book model doesnt contain a save method? If it is how do I mock the model correctly. I don't want it to touch the database (although it could if it has to).
Is it the multiple saves in the createTicket method?
Still learning how to set up tests correctly - slowly getting there but not enough knowledge yet.
If I change the name of the method in shouldReceive to say 'store' it still comes up with the save() error message.
Update:
I have isolated part of the problem to the createTicket call. I've changed my testStore test and updated as above.
My error with this current test is: Undefined index: start_number.
If I remove the call to createTicket in the controller method I don't get an error. I tried using Input::replace to replace the input from a form but appears not getting through to my function
How can I simulate a form input in the mocked objects?
Thanks

Categories