90.人机验证(二)

未匹配的标注

本节说明

  • 对应视频教程第 90 小节:Recaptcha Refactoring

本节内容

上一节我们引入了人机验证机制,本节我们来补上测试,并做些重构。首先我们增加人机验证的测试:
forum\tests\Feature\CreateThreadsTest.php

    .
    .
    /** @test */
    public function a_thread_requires_a_body()
    {
        $this->publishThread(['body' => null])
            ->assertSessionHasErrors('body');
    }

    /** @test */
    public function a_thread_requires_recaptcha_verification()
    {
        $this->publishThread(['g-recaptcha-response' => 'test'])
            ->assertSessionHasErrors('g-recaptcha-response');
    }
    .
    .

然后我们将人机验证的逻辑封装成一个单独的验证规则。新建验证规则:

$ php artisan make:rule Recaptcha

抽取验证逻辑:
forum\app\Rules\Recaptcha.php

    .
    .
    public function passes($attribute, $value)
    {
        $response = Zttp::asFormParams()->post('https://www.google.com/recaptcha/api/siteverify',[
            'secret' => config('services.recaptcha.secret'),
            'response' => $value,
            'remoteip' => request()->ip()
        ]);

        return $response->json()['success'];
    }

    public function message()
    {
        return 'The recaptcha verification failed.Try again.';
    }
}

应用验证规则:
forum\app\Http\Controllers\ThreadsController.php

    .
    .
    public function store(Request $request,Recaptcha $recaptch)
    {
        $this->validate($request,[
           'title' => 'required|spamfree',
            'body' => 'required|spamfree',
            'channel_id' => 'required|exists:channels,id',
            'g-recaptcha-response' => ['required',$recaptch]
        ]);

        $thread = Thread::create([
            'user_id' => auth()->id(),
            'channel_id' => request('channel_id'),
            'title' => request('title'),
            'body' => request('body')
        ]);

        if (request()->wantsJson()) {
            return response($thread,201);
        }

        return redirect($thread->path())
            ->with('flash','Your thread has been published!');
    }
    .
    .

注:头部引入use App\Rules\Recaptcha;

运行测试:
file
我们在创建话题时添加了新的验证规则,在CreateThreadsTest.php测试文件下的多个测试会报错,因为我们没有传入正确的token值。我们不需要在每个测试中都发送 Http 请求获取到正确的token值,所以我们来进行处理,只有在我们需要对 Http 请求进行测试时,我们才发送请求进行验证。
forum\tests\Feature\CreateThreadsTest.php

<?php

namespace Tests\Feature;

use App\Activity;
use App\Rules\Recaptcha;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class CreateThreadsTest extends TestCase
{
    use DatabaseMigrations;

    public function setUp()
    {
        parent::setUp();

        // 默认情况下,Recaptcha 验证规则都会通过
        app()->singleton(Recaptcha::class,function() {
           return \Mockery::mock(Recaptcha::class,function ($m) {
               $m->shouldReceive('passes')->andReturn(true);
           });
        });
    }
    .
    .
    /** @test */
    public function a_user_can_create_new_forum_threads()
    {
        $this->signIn();

        $thread = make('App\Thread');
        // 添加 g-recaptcha-response 即可通过验证
        $response = $this->post(route('threads'),$thread->toArray() + ['g-recaptcha-response' => 'token']);

        $this->get($response->headers->get('Location'))
            ->assertSee($thread->title)
            ->assertSee($thread->body);
    }
    .
    .
    /** @test */
    public function a_thread_requires_recaptcha_verification()
    {
        unset(app()[Recaptcha::class]);

        // 添加 g-recaptcha-response 即可通过验证
        $this->publishThread(['g-recaptcha-response' => 'test'])
            ->assertSessionHasErrors('g-recaptcha-response');
    }
    .
    .
    /** @test */
    public function a_thread_requires_a_unique_slug()
    {
        $this->signIn();

        create('App\Thread',[],2);

        $thread = create('App\Thread',['title' => 'Foo Title']);

        $this->assertEquals($thread->fresh()->slug,'foo-title');

        // 添加 g-recaptcha-response 即可通过验证
        $thread = $this->postJson(route('threads'),$thread->toArray() + ['g-recaptcha-response' => 'token'])->json();

        $this->assertEquals("foo-title-{$thread['id']}",$thread['slug']);
    }

    /** @test */
    public function a_thread_with_a_title_that_ends_in_a_number_should_generate_the_proper_slug()
    {
        $this->signIn();

        $thread = create('App\Thread',['title' => 'Something 24']);

        // 添加 g-recaptcha-response 即可通过验证
        $thread = $this->postJson(route('threads'),$thread->toArray() + ['g-recaptcha-response' => 'token'])->json();

        $this->assertEquals("something-24-{$thread['id']}",$thread['slug']);
    }
    .
    .
}

运行整个文件下的测试:
file
运行全部测试:
file

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~