简体   繁体   中英

PHPUnit Laravel Testing Controller that uses a Repository

I have read so many examples and cannot see what I am doing wrong, please if someone could help.

I am getting an error when running tests (error at the bottom of post), that doens't happen when viewing the page in the browser. I think this is because the repository isn't being instantiated properly so the relevant method not fired? Or some issue with the API call in the mock.

Controller:

namespace ShopApp\Http\Controllers\StoreFront;

use Illuminate\Http\Request;
use ShopApp\Http\Requests;
use ShopApp\Http\Controllers\Controller;
use ShopApp\Repositories\Contracts\CategoryRepositoryContract;
use ShopApp\Repositories\Contracts\PublicationRepositoryContract;

class PagesController extends Controller
{

    private $publication;
    private $category;


    public function __construct(PublicationRepositoryContract $publication, CategoryRepositoryContract $category){

    $this->publication = $publication;
    $this->category = $category;

    }

    /**
     * Homepage.
     * @return view
     * @internal param PublicationRepositoryContract $publication
     * @internal param CategoryRepositoryContract $category
     */
    public function home()
    {
        $mostRecent = $this->publication->getRecent();

        return view('pages/home')->with(compact('mostRecent'));

    }


}

Publication Repository:

<?php

namespace ShopApp\Repositories;

use ShopApp\Models\API\APIModel;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Support\Facades\Config;
use ShopApp\Repositories\Contracts\PublicationRepositoryContract;

class localPublicationRepository extends APIModel implements PublicationRepositoryContract
{


    private $end_point; // where are we talking to?
    public $response; //what did we get back?

    public function __construct(GuzzleClient $client){

        parent::__construct(new $client(['base_uri' => Config::get('customerprovider.local.api.base_uri'), 'http_errors' => true]));

        $this->end_point = 'Publications';

    }


    /**
     * Get all publications
     */
    public function getAll(){

        $this->response = $this->get($this->end_point);

        $publications_with_slugs = $this->assembleSlugs($this->response);

        return $publications_with_slugs;

    }


    /**
     * Get recent publications
     */
    public function getRecent(){

        return $this->getAll();  //@todo - update this to just get the most recent

    }


}

Test:

<?php

namespace Tests\Unit\Controllers;

use Tests\TestCase;
use Mockery as m;

class PagesControllerTest extends TestCase
{

    public $publicationRepositoryContract;

    /**
     * Setup mocks etc
     */
    public function setUp()
    {

        parent::setup();

        $this->publicationRepositoryContract = m::mock('ShopApp\Repositories\Contracts\PublicationRepositoryContract');

    }


    /**
     * Teardown mocks
     */
    public function tearDown()
    {
        m::close();
        parent::tearDown();
    }


    /**
     * A basic test example.
     *
     * @return void
     */
    public function testHomepage()
    {


        $this->publicationRepositoryContract
            ->shouldReceive('getRecent')
            ->once();

        $this->app->instance('ShopApp\Repositories\Contracts\PublicationRepositoryContract', $this->publicationRepositoryContract);

        $response = $this->call('GET', '/');

        $response->assertStatus(200);

        // getData() returns all vars attached to the response.
        $mostRecent = $response->original->getData()['mostRecent'];

        $response->assertViewHas('mostRecent');

        $this->assertInstanceOf('Array', $mostRecent);


    }


}

Test Error:

Expected status code 200 but received 500.
Failed asserting that false is true.
 /home/vagrant/Code/imsnews-site/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:61
 /home/vagrant/Code/imsnews-site/tests/Unit/Controllers/PagesControllerTest.php:53

Contents of Response ($response->Content()):

<span class="exception_title"><abbr title="ErrorException">ErrorException</abbr> in <a title="/home/vagrant/Code/imsnews-site/storage/framework/views/229655ca372490c9c0b1f5e7e2d4e91e6d3bbf6c.php line 262">229655ca372490c9c0b1f5e7e2d4e91e6d3bbf6c.php line 262</a>:</span>\n
                            <span class="exception_message">Invalid argument supplied for foreach() (View: /home/vagrant/Code/imsnews-site/resources/views/pages/home.blade.php)</span>\n

Line 262 from home.blade.php:

@foreach ($mostRecent as $key => $publication)

It seems clear that the method ->getRecent(), which in turn, calls ->getAll() on the publications repository is not returning an array as it should, but I don't know why.

Blade isn't complaining about the variable mostRecent not existing, it's complaining about it being invalid in a foreach.

Could this have something to do with Guzzle and the fact it's calling my API from the mocked test object?

Please help, hours have been lost..

Thanks.

Try mocking the concrete repository, and swap it out for the contract in the container. It seems you are mocking the contract, and then swapping it out for the same contract in your container.

TL;DR :

The key was you HAVE to have ->andReturn([]); on the test, like so:

$this->publicationRepositoryContract
            ->shouldReceive('getRecent')
            ->once()->andReturn([]);

My test only had:

$this->publicationRepositoryContract
                ->shouldReceive('getRecent')
                ->once();

Thanks to Ayo for pointing this out. It only became clear after deleting other parts of my test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM