模拟Laravel的Request :: segment方法

时间:2014-04-08 15:12:12

标签: php laravel mocking phpunit

这与this question有关,但遵循该解决方案并未解决我的问题。我也意识到Laravel's own documentation表示你不应该嘲笑Request对象,但我不确定如何编写这个测试。

这里是我要测试的代码的相似之处:

public function getThirdSegment()
{
    return Request::segment(3);
}

这是我目前的测试:

/**
 * @test
 */
public function indexReturnsOk()
{
    $this->prepareForTests();
    $this->call('GET', '/api/v1/courses');
    $this->assertResponseOk();
    $this->assertResponseStatus(200);
}

这是失败的,因为Request::segment(3)在运行PHPUnit时返回null。我首先尝试像这样模拟Request对象:

Request::shouldReceive('segment')->andReturn('courses');

但它仍然返回null。然后我尝试了这个:

$request = m::mock('Illuminate\Http\Request');
$request->shouldReceive('segment')->andReturn('courses');
Input::swap($request);

segment方法仍然返回null。有没有办法模拟这个方法的返回值?

更新

此代码属于服务提供商的注册方法,但我不认为这是问题的原因。从浏览器访问该站点可以实现我期望它做的事情,但运行PHPUnit似乎不会充实路由或URL,或任何与请求本身有关的事情。

2 个答案:

答案 0 :(得分:3)

到目前为止,最好的答案是我做错了。服务提供商在控制器加载之前就会运行,并且在进行单元测试时,Laravel的Illuminate\Foundation\Testing\TestCase加载应用程序(呼叫所有服务提供商' bootregister方法)在执行setUp方法期间,在执行任何单个测试期间可以进行任何调用之前的方式。

我尝试通过移动逻辑并找到一些工作来找到解决方案,这有点像:

class MyTestClass extends TestCase
{

    public function setUp()
    {
        // No call to parent::setUp()

        // From: Illuminate\Foundation\Testing\TestCase
        $this->app = $this->createApplication();
        $this->client = $this->createClient();
        // Not this one!
        //$this->app->setRequestForConsoleEnvironment();
        $this->app->boot();

        // ...
    }

    public function testWhatever()
    {
        // Calls to this will now actually have a request object
        $this->call('GET', '/api/v1/courses');
    }
}

但那不可能是正确的,至少它并不是这样。

相反,我认为最好不要依赖服务提供商内Request对象中的任何内容。相反,为什么不在我想要的控制器中注入一个可以做我需要它做的事情的对象呢?没有必要的服务提供商,我可以轻松地在单元测试中模拟Request以外的任何对象。我应该相信这些文档。

<强>更新

更进一步回答我自己的问题,我相信我最初的错误是我在服务提供商中使用Request对象。我认为,在回顾时,您可能永远不应该在服务提供者中使用Request对象,因为提供者会加载与laravel相关的所有内容(包括artisan命令,当然应该没有请求)。我的原始代码在浏览器中工作,但我可能会注意到我试图运行任何工匠命令的问题。

答案 1 :(得分:2)

我遇到了这个寻找处理使用请求的视图编写器的好方法,最终最终使用构造函数注入 - 因为Laravel使用Symfony Request类,它很容易“模拟“请求Request::create('http://myurl.com'),并将其传递到我的班级实例进行测试。比尝试在请求类上混乱使用存根方法要好得多,并且不依赖于构建整个Laravel应用程序。

为了清楚起见,视图编辑器看起来像这样:

class SiteLayoutComposer extends BaseComposer {

    function __construct(Request $request) {
        $this->request = $request;
    }

    public function compose($view)
    {
        $templateName = preg_match('/siteurlexample/', $this->request->getHost()) ? 'site1' : 'site2';
        $view->with('siteLayout', "layouts.sites.{$templateName}");
    }

}

测试:

class SiteLayoutComposerTest extends TestCase {

    public function testWillSetTheSiteLayoutToSite1()
    {
        $request = Request::create('http://www.siteurlexample.com');
        $view = View::make('home');
        $composer = new SiteLayoutComposer($request);
        $composer->compose($view);
        $this->assertEquals('layouts.sites.site1', $view->siteLayout);
    }

}