0

I am trying to test this function in a Laravel controller:

/**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store()
    {
        $project = $this->project->create(Input::all());
        if ( $errors = $project->errors()->all() ) {
           return Redirect::route('projects.create')
           ->withInput()
           ->withErrors($errors);

       }
       return Redirect::route('projects.index')
       ->with('flash', Lang::get('projects.project_created'));

   }

My (wrong) test looks like this:

public function testStoreFails()
    {
        $this->mock->shouldReceive('create')
            ->once()
            ->andReturn(Mockery::mock(array(
                    false,
                    'errors' => array()
                    )));
        $this->call('POST', 'projects');
        $this->assertRedirectedToRoute('projects.create');
        $this->assertSessionHasErrors();
    }

The problem is, basically, that Ardent sets $project to false when the validation fails, but it also sets errors on that same object, that can be retreived with ->errors()->all().

I'm quite lost trying to find out what to return in the mock.

Note: The constructor injects the model:

public function __construct(Project $project)
    {
        $this->project = $project;

        $this->beforeFilter('auth');
    }
Cmorales
  • 958
  • 1
  • 9
  • 24

1 Answers1

1

– Edited by following comments in answer –

If I'm understanding what you are trying to do properly, you could do this:

  • Mock the create() method to return a mocked Project.
  • Mock the save() method to return false.
  • Mock a MessageBag instance, which is the object that errors() would return. This mock should have a mocked all() method.
  • Make the mocked errors() method to return the MessageBag mock.

Assuming $this->mock is a mock of the Project class here:

// given that $this->mock = Mockery::mock('Project')

public function testStoreFails()
{
    // Mock the create() method to return itself
    $this->mock->shouldReceive('save')->once()->andReturn(false); 

    // Mock MessageBag and all() method
    $errors = Mockery::mock('Illuminate\Support\MessageBag');
    $errors->shouldReceive('all')->once()->andReturn(array('foo' => 'bar'));

    // Mock errors() method from model and make it return the MessageBag mock
    $this->mock->shouldReceive('errors')->andReturn($errors);

    // Proceed to make the post call
    $this->call('POST', 'projects');
    $this->assertRedirectedToRoute('projects.create');
    $this->assertSessionHasErrors();
}
Manuel Pedrera
  • 5,347
  • 2
  • 30
  • 47
  • PHP Fatal error: Call to a member function errors() on a non-object in /dumminvoicing/app/controllers/ProjectsController.php on line 43 – Cmorales Dec 10 '13 at 00:54
  • I guess the create method can't return just false, because in that case the errors method can't be called. – Cmorales Dec 10 '13 at 00:58
  • Ok, that makes sense. I read in your question that it was returned false and I got confused. Try with edited answer, I've moved the `create()` mock to top of the function and changed return value. I've also removed `once()` when mocking `errors()`, as I think the `withErrors()` method may call that again... just in case. – Manuel Pedrera Dec 10 '13 at 02:12
  • I get the same error :S Thanks for your help, I think we are closer at least. Maybe it's calling errors too early, or something like that? – Cmorales Dec 10 '13 at 17:01
  • Maybe the test is still writen in a wrong way but, looking in Ardent's code, there isn't any "create" method, nor I could find any reference in their docs. Maybe all the code is expected to run using "save" method. – Cmorales Dec 11 '13 at 00:16
  • Finally got it! I had to refactor my controller, this is the test: public function testStoreFails() { $this->mock->shouldReceive('save')->once()->andReturn(false); $errors = Mockery::mock('Illuminate\Support\MessageBag'); $errors->shouldReceive('all')->once()->andReturn(array('foo' => 'bar')); $this->mock->shouldReceive('errors')->andReturn($errors); $this->call('POST', 'projects'); $this->assertRedirectedToRoute('projects.create'); $this->assertSessionHasErrors(); } – Cmorales Dec 11 '13 at 01:05
  • You should post a new answer with the updated code, since your help guided me to get it working :) – Cmorales Dec 11 '13 at 01:06
  • Ok, it's updated. But just for the record, Ardent doesn't have a explicit `create()` method because it extends `Eloquent\Model`, but the method is still there and can be used too. – Manuel Pedrera Dec 11 '13 at 01:16
  • Yes, but my concern is if Ardent's validation (or behaviour) affects create method or not. With Ardent, save() returns true upon success. create() (by default) returns the created object, so how do you know if it's validated or not? That was the root of my problem and it's not clear at all in Ardent's docs. All examples show save() method, I couldn't find any using create(). – Cmorales Dec 11 '13 at 10:31