Laravel 项目:使用 TDD 构建论坛 Chapter 2
0.写在前面
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 。 - 视频源码地址:https://github.com/laracasts/Lets-Build-a-Forum-in-Laravel
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 Laravel 教程 - Web 开发实战进阶 ( Laravel 5.5 ) 类似,可互相参照
- 项目开发模式为
TDD
开发,教程简介为:A forum is a deceptively complex thing. Sure, it's made up of threads and replies, but what else might exist as part of a forum? What about profiles, or thread subscriptions, or filtering, or real-time notifications? As it turns out, a forum is the perfect project to stretch your programming muscles. In this series, we'll work together to build one with tests from A to Z.
- 项目版本为
laravel 5.4
,教程后面会进行升级到laravel 5.5
的教学 - 视频教程共计 102 个小节,笔记章节与视频教程一一对应
1.本节说明
- 对应视频第 2 小节:Testing Drving Threads
2.本节内容
第一个功能测试
测试环境:
在运行测试时,Laravel 会根据 phpunit.xml 文件中设定好的环境变量自动将环境变量设置为 testing,并将 Session 及缓存以 array 的形式存储,也就是说在测试时不会持久化任何 Session 或缓存数据。
你可以随意创建其它必要的测试环境配置。testing 环境的变量可以在 phpunit.xml 文件中被修改,但是在运行测试之前,请确保使用 config:clear Artisan 命令来清除配置信息的缓存。
以上是 Laravel 5.5 中文文档 中对 Laravel 测试 的简介,详细内容参见文档 。
现在来建立第一个简单的功能测试:a_user_can_browse_theads
。首先重命名tests\Feature\ExampleTest.php
为ThreadsTest.php
,修改phpunit.xml
文件,配置测试环境:
.
.
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="MAIL_DRIVER" value="array"/>
</php>
.
.
编写测试方法:
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ThreadsTest extends TestCase
{
use DatabaseMigrations;
/** @test */
public function a_user_can_browse_threads()
{
$response = $this->get('/threads');
$response->assertStatus(200);
}
}
找到Illuminate\Foundation\Testing\DatabaseMigrations.php
文件:
<?php
namespace Illuminate\Foundation\Testing;
use Illuminate\Contracts\Console\Kernel;
trait DatabaseMigrations
{
/**
* Define hooks to migrate the database before and after each test.
*
* @return void
*/
public function runDatabaseMigrations()
{
$this->artisan('migrate');
$this->app[Kernel::class]->setArtisan(null);
$this->beforeApplicationDestroyed(function () {
$this->artisan('migrate:rollback');
});
}
}
可以看到,每次在进行测试的时候,都会执行php artisan migrate
命令初始化数据库;每次执行完测试,都会执行php artisan migrate:rollback
命令重置数据库。
现在执行命令运行测试:
$ phpunit
会发现有报错,这是理所应该的,因为目前我们还未设置路由。前往web.php
文件添加路由配置:
Route::get('/threads','ThreadsController@index');
前往ThreadsController.php
添加index
方法:
public function index()
{
$threads = Thread::latest()->get();
return view('threads.index',compact('threads'));
}
注:
latest
和oldest
方法允许你轻松地按日期对查询结果排序。默认情况下是对 created_at 字段进行排序。或者,你可以传递你想要排序的字段名称:$user = DB::table('users') ->latest() ->first();
新建视图文件resources/views/threads/index.blade.php
:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">forum Threads</div>
<div class="panel-body">
[@foreach](https://learnku.com/users/5651) ($threads as $thread)
<article>
<a href="/threads/{{ $thread->id }}">
<h4>{{ $thread->title }}</h4>
</a>
<div class="body">{{ $thread->body }}</div>
</article>
<hr>
@endforeach
</div>
</div>
</div>
</div>
</div>
@endsection
顺手生成一下Laravel
自带的注册登录功能:
$ php artisan make:auth
再次执行命令即可成功运行:
即意味着访问 http://forum.test/threads ,可以看到:
此时的功能测试仅仅代表可以访问该路由,并未达到功能测试的要求。接下来 编写真正的测试逻辑:
public function a_user_can_browse_threads()
{
$thread = factory('App\Thread')->create();
$response = $this->get('/threads');
$response->assertSee($thread->title);
}
运行测试:
$ phpunit
测试通过:
此时如果我们去掉index.blade.php
视图中title
属性,则应该测试失败:
.
.
<div class="panel-body">
[@foreach](https://learnku.com/users/5651)($threads as $thread)
<article>
//
</article>
<hr>
@endforeach
.
.
运行测试,发现测试失败:
证明我们的测试有效。
继续编写测试,测试单个thread
:
public function a_user_can_browse_threads()
{
$thread = factory('App\Thread')->create();
$response = $this->get('/threads');
$response->assertSee($thread->title);
$response = $this->get('/threads/' . $thread->id);
$response->assertSee($thread->title);
}
运行测试依然会失败,因为还未添加路由、控制器方法跟视图。
修改web.php
:
.
.
Route::get('/threads','ThreadsController@index');
Route::get('/threads/{thread}','ThreadsController@show');
修改ThreadsController.php
:
.
.
public function show(Thread $thread)
{
return view('threads.show',compact('thread'));
}
.
.
新建resources/views/threads/show.blade.php
:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
{{ $thread->title }}
</div>
<div class="panel-body">
{{ $thread->body }}
</div>
</div>
</div>
</div>
</div>
@endsection
再次测试,测试成功:
此时访问 http://forum.test/threads/1 :
此时我们的测试是放在一个方法中的,不但可读性不高,并且在功能测试不通过时难以准确定位,于是将a_user_can_browse_threads
测试拆分成a_user_can_view_all_threads
和a_user_can_read_single_thread
两个测试:
use DatabaseMigrations;
/** @test */
public function a_user_can_view_all_threads()
{
$thread = factory('App\Thread')->create();
$response = $this->get('/threads');
$response->assertSee($thread->title);
}
/** @test */
public function a_user_can_read_a_single_thread()
{
$thread = factory('App\Thread')->create();
$response = $this->get('/threads/' . $thread->id);
$response->assertSee($thread->title);
}
运行测试,成功通过测试:
此时在我们的视图文件中,我们采用的是使用url
的方式给文章标题附上超链接。可是这种方法可读性差且不利于维护,现在进行修改。
首先在app\Thread.php
模型中新增path
方法,用来获取链接:
.
.
public function path()
{
return '/threads/'.$this->id;
}
.
接着修改视图文件index.blade.php
:
.
.
<article>
<a href="{{ $thread->path() }}">
<h4>{{ $thread->title }}</h4>
</a>
<div class="body">{{ $thread->body }}</div>
</article>
.
.
3.笔记心得
-
注 1
:
页首的样式可以使用 Laravel 教程 - Web 开发实战进阶 ( Laravel 5.5 ) 教程中的样式,运行Laravel Mix
可以参考参考课程下的讨论:
问答:[已解决] 安装 Laravel-MIX 百般尝试依旧问题重重,请各位老鸟,帮忙... -
首次接触
TDD
实战,期待看看能带来什么 :smile:
4.写在后面
- 如有建议或意见,欢迎指出~
- 如果觉得文章写的不错,请点赞鼓励下哈,你的鼓励将是我的动力!
本作品采用《CC 协议》,转载必须注明作者和本文链接