PHPUnit Laravel 使用存储库的测试控制器
PHPUnit Laravel Testing Controller that uses a Repository
我看了这么多例子,看不出我做错了什么,如果有人能帮忙的话。
我在 运行 测试时遇到错误(post 底部的错误),在浏览器中查看页面时不会发生这种情况。我认为这是因为存储库没有被正确实例化,所以相关方法没有被触发?或者模拟中 API 调用的一些问题。
控制器:
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'));
}
}
出版物存储库:
<?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
}
}
测试:
<?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);
}
}
测试错误:
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
响应内容($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
第 262 行来自 home.blade.php:
@foreach ($mostRecent as $key => $publication)
似乎很明显,方法 ->getRecent() 反过来调用了出版物存储库上的 ->getAll() 并没有按应有的方式返回数组,但我不知道为什么。
Blade 并没有抱怨 mostRecent 变量不存在,而是抱怨它在 foreach 中无效。
这可能与 Guzzle 以及它从模拟测试对象调用我的 API 这一事实有关吗?
请帮忙,时间已经丢失了..
谢谢。
尝试模拟具体存储库,并将其替换为容器中的合约。看起来你在嘲笑合同,然后将它换成你容器中的同一个合同。
长话短说:
关键是你必须有 ->andReturn([]);在测试中,像这样:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once()->andReturn([]);
我的测试只有:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once();
感谢 Ayo 指出这一点。只有在删除我测试的其他部分后,它才变得清晰。
我看了这么多例子,看不出我做错了什么,如果有人能帮忙的话。
我在 运行 测试时遇到错误(post 底部的错误),在浏览器中查看页面时不会发生这种情况。我认为这是因为存储库没有被正确实例化,所以相关方法没有被触发?或者模拟中 API 调用的一些问题。
控制器:
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'));
}
}
出版物存储库:
<?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
}
}
测试:
<?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);
}
}
测试错误:
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
响应内容($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
第 262 行来自 home.blade.php:
@foreach ($mostRecent as $key => $publication)
似乎很明显,方法 ->getRecent() 反过来调用了出版物存储库上的 ->getAll() 并没有按应有的方式返回数组,但我不知道为什么。
Blade 并没有抱怨 mostRecent 变量不存在,而是抱怨它在 foreach 中无效。
这可能与 Guzzle 以及它从模拟测试对象调用我的 API 这一事实有关吗?
请帮忙,时间已经丢失了..
谢谢。
尝试模拟具体存储库,并将其替换为容器中的合约。看起来你在嘲笑合同,然后将它换成你容器中的同一个合同。
长话短说:
关键是你必须有 ->andReturn([]);在测试中,像这样:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once()->andReturn([]);
我的测试只有:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once();
感谢 Ayo 指出这一点。只有在删除我测试的其他部分后,它才变得清晰。