简体   繁体   English

控制器的 Laravel 单元测试

[英]Laravel unit testing of controllers

I am trying to start a new Laravel app following TDD我正在尝试在 TDD 之后启动一个新的 Laravel 应用程序

My first step is to check that the /login controller is called on the home url.我的第一步是检查 /login 控制器是否在主页 url 上被调用。

Despite following several tutorials I can't get the test to work and I can't see what I'm doing wrong at all.尽管遵循了几个教程,但我无法让测试工作,我根本看不出我做错了什么。

My set up is: composer to install laravel composer to install phpunit我的设置是:composer安装laravel composer安装phpunit

here is my route:这是我的路线:

<?php
Route::get('/login', 'AuthenticationController@login');

my controller:我的控制器:

<?php

class AuthenticationController extends BaseController {

    public function login () {
        return View::make('authentication.login');
    }

}

And my test:我的测试:

<?php

class AuthenticationTest extends TestCase {

    public function testSomeTest () {

        $response = $this->action('GET', 'AuthenticationController@login');

        $view = $response->original;

        $this->assertEquals('authentication.login', $view['name']);
    }
}

The error I get is我得到的错误是

  ErrorException: Undefined index: name

The code as a copy (pretty much exactly) from the Laravel site, yet it doesn't run.代码是来自 Laravel 站点的副本(几乎完全相同),但它没有运行。

Can anyone see what I'm doing wrong?谁能看到我做错了什么?

It claims $view has no index name, but that can't be right as its the example on the laravel website, plus the view is being rendered using its name (it is showing correctly on the front end too)它声称 $view 没有索引名称,但这不能像 laravel 网站上的示例那样正确,而且视图是使用其名称呈现的(它也在前端正确显示)

EDIT::编辑::

So it seems from a comment that the laravel unit testing section isn't clear and that the $view['name'] is checking for a variable called $name.因此,从评论看来,laravel 单元测试部分不清楚,并且 $view['name'] 正在检查名为 $name 的变量。 If that is the case, how do you test the controller/route used, IE.如果是这种情况,您如何测试所使用的控制器/路由,IE。 what controller name/action name has been used for route('X')什么控制器名称/动作名称已用于路由('X')

Update, 2020-07-01:更新,2020-07-01:

Since this answer seems to still get some upvotes every now and then, I just want to point out that I do not consider this to be a good testing approach anymore.由于这个答案似乎仍然时不时地得到一些赞成,我只想指出我不再认为这是一个好的测试方法 Laravel has massively improved on the testing experience since v4 and there was a pretty significant overall paradigm shift, away from units and classes and more towards features and endpoints.自 v4 以来,Laravel 在测试体验上有了很大改进,并且整体范式发生了相当显着的转变,从单元和类转向功能和端点。 That is not only an idiomatic change but also seems to make way more sense from a technical perspective.这不仅是惯用的变化,而且从技术角度来看似乎更有意义。

Also, a lot of new and useful testing helpers that allow for less brittle tests have been introduced since then.此外,从那时起,还引入了许多新的和有用的测试助手,允许进行不那么脆弱的测试。 Please refer to the documentation for an overwiew and basic test examples.有关概述和基本测试示例,请参阅文档。


Ok, as already explained a little in the comments, let's first take a step back and think about the scenario.好的,正如评论中已经解释的那样,让我们​​先退后一步考虑一下场景。

"My first step is to check that the /login controller is called on the home url." “我的第一步是检查 /login 控制器是否在主页 url 上被调用。”

So that means: When the user hits the home route, you want to check if the user is logged in. If he's not, you want to redirect them to the login, maybe with some flash message.所以这意味着:当用户点击回家路线时,你想检查用户是否登录。如果他没有登录,你想将他们重定向到登录,也许有一些闪现消息。 After they have logged in, you want to redirect them back to the home page.在他们登录后,您希望将他们重定向回主页。 If the login fails, you want to redirect them back to the login form, maybe also with a flash message.如果登录失败,您希望将它们重定向回登录表单,也可以使用闪现消息。

So there are several things to test now: The home controller and the login controller.所以现在有几件事要测试:家庭控制器和登录控制器。 So following the TDD spirit, let's create the tests first.因此,遵循 TDD 精神,让我们首先创建测试。

Note: I'll follow some naming convention that is used by phpspec , but don't let that bother you.注意:我将遵循phpspec使用的一些命名约定,但不要让它打扰您。

class HomeControllerTest extends TestCase
{
    /**
     * @test
     */
    public function it_redirects_to_login_if_user_is_not_authenticated()
    {
        Auth::shouldReceive('check')->once()->andReturn(false);

        $response = $this->call('GET', 'home');
        
        // Now we have several ways to go about this, choose the
        // one you're most comfortable with.

        // Check that you're redirecting to a specific controller action 
        // with a flash message
        $this->assertRedirectedToAction(
             'AuthenticationController@login', 
             null, 
             ['flash_message']
        );
        
        // Only check that you're redirecting to a specific URI
        $this->assertRedirectedTo('login');

        // Just check that you don't get a 200 OK response.
        $this->assertFalse($response->isOk());

        // Make sure you've been redirected.
        $this->assertTrue($response->isRedirection());
    }

    /**
     * @test
     */
    public function it_returns_home_page_if_user_is_authenticated()
    {
        Auth::shouldReceive('check')->once()->andReturn(true);

        $this->call('GET', 'home');

        $this->assertResponseOk();
    }
}

And that's it for the Home controller.这就是 Home 控制器。 In most cases you actually don't care where you are redirected to, because that may change over time and you would have to change the tests.在大多数情况下,您实际上并不关心您被重定向到哪里,因为这可能会随着时间的推移而改变,您将不得不更改测试。 So the least you should do is to check whether you are being redirected or not and only check for more details if you really think that it matters for your test.因此,您至少应该检查您是否被重定向,并且仅在您确实认为这对您的测试很重要时才检查更多详细信息。

Let's have a look at the Authentication controller:让我们来看看身份验证控制器:

class AuthenticationControllerTest extends TestCase
{
    /**
     * @test
     */
    public function it_shows_the_login_form()
    {
        $response = $this->call('GET', 'login');

        $this->assertTrue($response->isOk());

        // Even though the two lines above may be enough,
        // you could also check for something like this:

        View::shouldReceive('make')->with('login');
    }

    /**
     * @test
     */
    public function it_redirects_back_to_form_if_login_fails()
    {
        $credentials = [
            'email' => 'test@test.com',
            'password' => 'secret',
        ];

        Auth::shouldReceive('attempt')
             ->once()
             ->with($credentials)
             ->andReturn(false);

        $this->call('POST', 'login', $credentials);

        $this->assertRedirectedToAction(
            'AuthenticationController@login', 
            null, 
            ['flash_message']
        );
    }

    /**
     * @test
     */
    public function it_redirects_to_home_page_after_user_logs_in()
    {
        $credentials = [
            'email' => 'test@test.com',
            'password' => 'secret',
        ];

        Auth::shouldReceive('attempt')
             ->once()
             ->with($credentials)
             ->andReturn(true);

        $this->call('POST', 'login', $credentials);

        $this->assertRedirectedTo('home');
    }
}

Again, always think about what you really want to test.同样,始终考虑您真正想要测试的内容。 Do you really need to know which controller action is triggered on which route?真的需要知道在哪个路由上触发了哪个控制器操作吗? Or what the name of the view is that is returned?或者返回的视图名称是什么? Actually, you just need to make sure that the controller actually attempts to do it.实际上,您只需要确保控制器确实尝试这样做。 You pass it some data and then test if it behaves as expected.您向它传递一些数据,然后测试它是否按预期运行。

And always make sure you're not trying to test any framework functionality, for instance if a specific route triggers a specific action or if a View is loaded correctly.并且始终确保您没有尝试测试任何框架功能,例如特定路由是否触发特定操作或视图是否正确加载。 This has already been tested, so you don't need to worry about that.这已经过测试,因此您无需担心。 Focus on the functionality of your application and not on the underlying framework.专注于应用程序的功能,而不是底层框架。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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