原文地址:
Note 本系列第四节内容.
本章会创建一个以后可以用到的项目便于以后我们的课程使用, 同时也会查课各种测试选项. 以后一段时间内会开发一个 文本转换成 Html 的服务信息.
创建 l5beauty 项目
根据之前的章节 Six Steps to Starting a New Laravel 5.1 Project 创建 l5beauty 项目下面所示项目
首先在你的系统安装 app 框架
Step 1 - 安装项目框架
$ laravel new l5beautyCrafting application...Generating optimized class loaderCompiling common classesApplication key [rzUhyDksVxzTXFjzFYiOWToqpunI2m6X] set successfully.Application ready! Build something amazing.
接下来设置 l5beauty.app
作为虚拟主机.
Step 2 - 配置服务器
$ serve l5beauty.app ~/l5beauty/publicdos2unix: converting file /vagrant/scripts/serve.sh to Unix format ... * Restarting nginx nginx [ OK ]php5-fpm stop/waitingphp5-fpm start/running, process 2169
回到主机, 添加以下记录到映射文件
Step 3 - 添加 l5beauty.app 到映射文件
192.168.10.10 l5beauty.app
从主机中, 按照以下步骤安装 NPM 本地包
Step 4 - NPM 本地安装
$ cd l5beauty$ npm install|> node-sass@2.0.1 install /Users/chuck/Code/l5beauty/node_modules/laravel-\ elixir/node_modules/gulp-sass/node_modules/node-sass> node scripts/install.js> node-sass@2.0.1 postinstall /Users/chuck/Code/l5beauty/node_modules/\ laravel-elixir/node_modules/gulp-sass/node_modules/node-sass> node scripts/build.js`darwin-x64-node-0.10` exists; testingBinary is fine; exitinggulp@3.8.11 node_modules/gulp├── v8flags@2.0.2├── pretty-hrtime@0.2.2[snip]
回到主机, 创建数据库
Step 5 - 创建应用数据库
$ mysql --user=homestead --password=secretmysql> create database l5beauty;Query OK, 1 row affected (0.00 sec)mysql> exit;Bye
编辑 .env
文件, 修改数据库为 l5beauty
.
Step 6 - 更改配置文件中的 DB_NAME
// Change the following lineDB_DATABASE=homestead// To the correct valueDB_DATABASE=l5beauty
最后, 访问 http://l5beauty.app
, 确保一切可用.
Figure 6.1 - Step 5 - 在浏览器中测试
运行 PHPUnit
Laravel 5.1 已经准备好测试了, 有个最简单的方式来检测一个访问是否返回200响应.
运行 PHPUnit , 简单的在根目录上执行 phpunit
就OK
运行 PHPUnit
$ cd l5beauty~/l5beauty $ phpunitPHPUnit 4.7.4 by Sebastian Bergmann and contributors.Time: 544 ms, Memory: 10.25MbOK (1 test, 2 assertions)
是否有错误 ?
如果你在运行 phpunit
收到 command not found 或者 permissions denied 错误提示, 有可能是因为安装问题. phpunit
命令一般会存放在 vendor/bin
目录并且添加进系统变量, 问题是 Laravel 命令有一个bug是没有给这个命令设置相应的权限
使用如下方法解决这个问题:
Step 1 - 删除 vendor
目录
Step 2 - 在代码根目录使用 composer update
命令重新创建 vendor
目录. (操作系统中运行)
这样, 然后重新执行 phpunit
Laravel 5.1 PHPUnit 配置
在 Laravel 5.1 项目根目录中有个文件 phpunit.xml
. 这个文件包含使用 phpunit
运行时候的配置
phpunit.xml
的测试会放置在 tests
目录, 这里有两个文件
-
ExampleTest.php
- 包含一个测试方法testBasicExample()
. 这个ExampleTest
类集成自TestCase
类. -
TestCase.php
- Laravel 基础测试单元.
查看 testBasicExample()
方法 ExampleTest.php
.
testBasicExample() 方法
public function testBasicExample() { $this->visit('/') ->see('Laravel 5'); }
这个测试告诉我们 "访问主页并且能够看到内容 ‘Laravel 5’", 还能比这个更简洁么 ?
TestCase
类提供在框架中的应用方法和属性. TestCase
同样提供了一个附加断言列表方法和 crawler 类型测试
Laravel 5.1 Crawler 方法和属性
Crawler 允许你测试web应用. 这些方法都有个统一的优点就是都能够返回 $this
, 允许你创建 ->visit()->see()
类似这样的链式调用.
下边是一些属性和方法:
$this->response
web应用返回的最后的响应
$this->currentUri
当前查看的Uri
visit($uri)
(Fluent) 使用 get 方法访问给定的uri
get($uri, array $headers = [])
(Fluent) 使用 get 方法访问url, 并可以传输给定的header
post($uri, array $data = [], array $headers = [])
(Fluent) post 请求
put($uri, array $data = [], array $headers = [])
(Fluent) put 请求
patch($uri, array $data = [], array $headers = [])
(Fluent) PATCH 请求
delete($uri, array $data = [], array $headers = [])
(Fluent) DELETE 请求
followRedirects()
(Fluent) 跟踪最近返回的重定向
see($text, $negate = false)
(Fluent) 查找页面上显示的内容/显示/不显示
seeJson(array $data = null)
(Fluent) 判定请求包含 json, 如果传输了 $data
参数, json 值必须匹配.
seeStatusCode($status)
(Fluent) 相应是否返回指定的状态码
seePageIs($uri)
(Fluent) 当前页面是否是指定的URI
seeOnPage($uri)
and landOn($uri)
(Fluent) Alias seePageIs()
click($name)
(Fluent) 通过name 或者 id 来请求点击
type($text, $element)
(Fluent) 填充自定义的文本
check($element)
(Fluent) 检测页面的checkbox
select($option, $element)
attach($absolutePath, $element)
(Fluent) 附件
press($buttonText)
(Fluent) 提交指定文本的text
withoutMiddleware()
(Fluent) 禁用 middleware
dump()
输入最近返回的内容
Laravel 5.1 PHPUnit 应用方法
这里有额外的 Laravel 5.1 方法和属性
$app
$app 实例
$code
artisan 返回的最近的 code 码
refreshApplication()
刷新应用, setup()方法会自动调用这个方法.
call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
调用指定的url 并且返回响应.
callSecure($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
调用 https 访问url并且返回响应
action($method, $action, $wildcards = [], $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
调用控制器方法
route($method, $name, $routeParameters = [], $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
调用路由并且返回方法.
instance($abstract, $object)
注册对象的实例
expectsEvents($events)
指定可能被触发的事件列表.
withoutEvents()
不调用事件
expectsJobs($jobs)
注册队列
withSession(array $data)
使用session
session(array $data)
开始并设置 session
flushSession()
刷新当前session 的内容
startSession()
开始 session
actingAs($user)
(Fluent) 设置当前登录的用户
be($user)
设置用户
seeInDatabase($table, array $data, $connection = null)
(Fluent) 检测给定的数据是否存在在数据库中
notSeeInDatabase($table, $array $data, $connection = null)
(Fluent) 检测数据是否不存在数据库中
missingFromDatabase($table, array $data, $connection = null)
(Fluent) Alias notSeeInDatabase()
.
seed()
数据库数据seed 生成器
artisan($command, $parameters = [])
指定 artisan 命令并且返回代码
上边的方法/属性都能够在 test 中 使用, 默认的 ExampleTest.php
中存在一个方法 testBasicExample()
, 这个调用了 $this->call(...)
方法.
Laravel 5.1 PHPUnit 断言
除了标准的 PHPUnit 断言(assertEquals()
, assertContains()
, assertInstanceOf()
, ...)之外, 还存在很多允许测试 web 应用的检测项目
assertPageLoaded($uri, $message = null)
检测最近的页面是否被加载, 如果不存在 url / message 时候会报错
assertResponseOk()
是否页面相应OK
assertReponseStatus($code)
是否响应指定的code
assertViewHas($key, $value = null)
视图中是否存在指定的数据
assertViewHasAll($bindings)
视图中是否存在指定的一系列数据
assertViewMissing($key)
指定视图中是否不存在这个数据
assertRedirectedTo($uri, $with = [])
检测是否重定向到指定的uri
assertRedirectedToRoute($name, $parameters = [], $with = [])
是否客户端重定向到指定的路由
assertRedirectedToAction($name, $parameters = [], $with = [])
是否重定向到 action
assertSessionHas($key, $value = null)
session 中是否存在 key/ value
assertSessionHasAll($bindings)
session 中是否存在指定的 kv
assertSessionHasErrors($bindings = [])
session 是否存在错误
assertHasOldInput()
session 中是否存在以前的数据
使用 Gulp 来进行 TDD 测试
是用javascript 写成的编译和自动化工具. 基本用来最小化源代码或者从源代码生成文件. Gulp 能够监控源代码的改变并且自动运行指定的任务
Laravel 5.1 存在 允许运行 gulp 任务. Elixir 加入了更简洁的语法. 你这样想 PHP 中的 Laravel, Gulp 中的 Elixir.
一个最常用的 Gulp 是自动化测试. 我们根据 TDD (Test Driven Development) 来自动化运行我们的测试任务.
首先, 编辑 gulpfile.js
文件, 并按照以下修改.
配置 Gulp 来运行 PHPUnit 测试
var elixir = require('laravel-elixir');elixir(function(mix) { mix.phpUnit();});
这里我们调用 elixir()
方法. 传递一个函数. 这个函数接收一个 mix 对象. 这个函数能够干很多你可能想都想不到的事情. 你可能想通过 less 文件编译 css 文件. 然后合并 css 文件, 然后再文件末尾加缀上版本号. 所有的这些事情都可以通过 mix
对象来运行.
但是现在, 我们仅仅运行 PHPUnit 测试.
接下来, 直接运行 glup
运行 Gulp
~% cd Code/l5beauty~/Code/l5beauty% gulp[15:26:23] Using gulpfile ~/Code/l5beauty/gulpfile.js[15:26:23] Starting 'default'...[15:26:23] Starting 'phpunit'...[15:26:25] Finished 'default' after 2.15 s[15:26:25]*** Debug Cmd: ./vendor/bin/phpunit --colors --debug ***[15:26:28] PHPUnit 4.7.4 by Sebastian Bergmann and contributors.Configuration read from /Users/chuck/Code/l5beauty/phpunit.xmlStarting test 'ExampleTest::testBasicExample'.Time: 2.07 seconds, Memory: 10.25MbOK (1 test, 2 assertions)[15:26:28] gulp-notify: [Green!][15:26:28] Finished 'phpunit' after 4.96 s
你可能收到一个通知, 一个弹框, 绿色告知你所有测试都已经通过了
Figure 6.2 - Gulp’s PHPUnit Success on Windows 8.1
想要使用 gulp进行自动化测试, 运行 gulp tdd
运行 Gulp
~% cd Code/l5beauty~/Code/l5beauty% gulp tdd[15:29:49] Using gulpfile ~/Code/l5beauty/gulpfile.js[15:29:49] Starting 'tdd'...[15:29:49] Finished 'tdd' after 21 ms
这个命令挂载在这里, 监听源文件的变化, 并且在需要的时候进行单元测试.
想要查看如何运行的. 让我们中段存在的单元测试.
改变 tests/ExampleTest.php
的 see()
方法.
中段 ExampleTest.php
->see('Laravel 5x');
当你保存这个文件, gulp 将会通知并且重新运行 PHPUnit , 这个将会执行错误. 然后你会看到一个红色的错误提示
Figure 6.3 - Gulp’s PHPUnit Failure on Mac
如果你要重新更改回来,保存, 然后 gulp 会重新运行 PHPUnit, 然后你会看到绿色的图标
退出 Gulp’s tdd 模式
按下 Ctrl+C
创建 Markdown 服务
在博客应用中我们会使用 语法来写文章, 如果你不熟悉 markdown, 可以通过连接来检查这个语法, 这是一个快速读/写并且能够保存为 HTML.
举例说明, 我们会创建一个服务项目来生成HTML.
拉取一个 Markdown 包
这里有许多的PHP包来把markdown 转换为HTML, 如果你去 http://packagist.org
这里搜索 markdown , 会发现 20 多页的包.
我们会使用 Michel Fortin 创建的包. 因为"好", 接下来我们运行如下的命令来拉取这个包
添加 Markdown 和 SmartyPants
~/Code/l5beauty% composer require michelf/php-markdownUsing version ^1.5 for michelf/php-markdown./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing michelf/php-markdown (1.5.0) Downloading: 100%Writing lock fileGenerating autoload filesGenerating optimized class loader~/Code/l5beauty% composer require "michelf/php-smartypants=1.6.0-beta1"./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing michelf/php-smartypants (1.6.0-beta1) Loading from cacheWriting lock fileGenerating autoload filesGenerating optimized class loader
你有没有注意到指定了包版本号. 这是因为写这篇文章的时候还没有一个稳定的版本号能够自动拉取到
创建 Markdown 测试类
开始 TDD session 的第一件事就是开启 TDD 模式
Starting Gulp in TDD mode
~/Code/l5beauty% gulp tdd[19:41:38] Using gulpfile ~/Code/l5beauty/gulpfile.js[19:41:38] Starting 'tdd'...[19:41:38] Finished 'tdd' after 23 ms
现在 gulp 监控 PHPUnit 的改变. 让我们创建测试类
在 tests
目录, 创建一个新文件夹命名为 Services
同时创建一个文件 MarkdownerTest.php
初始化 tests/Services/MarkdownerTest.php
markdown = new \App\Services\Markdowner(); } # 测试 public function testSimpleParagraph() { $this->assertEquals( "test
\n", $this->markdown->toHTML('test') ); }}
这里会报错
即使告诉你检测失败, 日志中也会存在相应的错误日志, 这里很明显的是 App\Services\Markdowner
这个类不存在.
创建 Markdowner 服务
这里我们创建一个服务来封装 php-markdown 和 php-smartypants 来加载导入的服务
在 app\Services
目录创建一个 Markdowner.php
服务并填写以下的内容 .
app/Services/Markdowner 的内容
preTransformText($text); $text = MarkdownExtra::defaultTransform($text); $text = SmartyPants::defaultTransform($text); $text = $this->postTransformText($text); return $text; } protected function preTransformText($text) { return $text; } protected function postTransformText($text) { return $text; }}
当你保存了这个文件, Gulp 应当显示一个绿色的提示框. 告诉你执行OK.
如果你没有收到绿色的提示. 检测文件和测试类.
更多测试
大家都同意的是, 这不是一个好的 TDD 测试例子, 因为太简单了. 实际的会有好多操作步骤和迭代, 如下
- 创建 MarkdownerTest w/ testSimpleParagraph()
- Tests Fail
- 创建 Markdowner 类, 硬编码 toHTML() 来通过测试
- Tests Succeed
- 更新 Markdowner 类来使用 MarkdownExtra
- Tests Succeed
- 添加一个 testQuotes() 到 MarkdownerTest 类
- Tests Fail
- 更新 Markdowner 类来使用 SmartyPants
- Tests Succeed
目前为止, 所有的我们 Markdowner
类在测试前都是不完整的.要在该类上进行 纯 单元测试,应该将其结构化以便将 MarkdownExtra
和 SmartyPants
类的实例注入到构造函数中, 通过这种方式我们的单元测试可以注入模拟对象,并且只验证 MarkdownExtra
的行为,而不是它所调用的类.
但这不是一本关于测试的书。事实上,这是讨论测试的惟一一章。我们会离开这个结构,但需要再添加几个测试。
更新 MarkdownerTest
来和下面的内容一致
app/Services/Markdowner 的最终内容
markdown = new \App\Services\Markdowner(); } /** * @dataProvider conversionsProvider */ public function testConversions($value, $expected) { $this->assertEquals($expected, $this->markdown->toHTML($value)); } public function conversionsProvider() { return [ ["test", "test
\n"], ["# title", "title
\n"], ["Here's Johnny!", "Here’s Johnny!
\n"], ]; }}
在这里,我们更改了测试类用来一次测试多个转换,并在 conversionsProvider()
中添加了三个测试。在进行下一步骤之前,你的测试结果应该是绿色的。
在系统控制台中一旦测试是绿色的,按 Ctrl+C
来停止 Gulp
测试的其他方法
这里并不是想使用 Laravel 5.1
提供一个完整的测试方法,因为在PHP中没有单独的方法来进行测试。
因此,在 Laravel 5 5.1 中没有单一的测试方法。
但是,我们将探索一些替代方案
phpspec
除了 PHPUnit ,Laravel 5.1 还提供了 。这是另一种流行的PHP测试,它更多地关注于 BDD(行为驱动开发)
这里有一些关于phpspec的注释。
- 程序文件在
vendor/bin
, 因此你可以在项目根目录下调用phpspec
. - 配置文件在根目录, 名字是
phpspec.yml
. - 从 Gulp 中运行 phpspec , Elixir 提供了
phpSpec()
函数, 你可以在mix
对象中运行. - 如果你把程序的命名空间从
App
改成其他的命名空间, 确保同步更新phpspec.yml
中的配置.
单元测试
虽然 PHPUnit 是 PHP 单元测试的标准,但也有其他的包可以使用
- - 单元测试框架支持 mocks 和 stubs.
- - 另一个使用 mock 的单元测试框架.
功能 / 验收测试
这些测试真实的使用了您的应用程序,而不是仅仅验证您的应用程序中的代码单元。当使用流畅的测试方法 Laravel 5.1 时,你可以使用 PHPUnit 进行一些功能测试. ExampleTest.php
提供了一个简单的示例. 但是也有其他的测试框架关注于功能/验收测试(functional / acceptance testing).
- - 验收测试最流行的框架.
- - 浏览器自动化.
- - 浏览器自动化.
行为驱动开发 (BDD)
BDD(行为驱动开发)有两种方式: SpecBDD 和 StoryBDD
SpecDD 关注代码的技术方面。Laravel 5.1 包含了 _phpspec_, 这是SpecDD的标准
StoryBDD 强调业务或特性测试. 是最流行的 StoryBDD 框架。同样 也可以用于 StoryBDD 。
回顾
我们在这一章所做的第一件事就是创建一个名为 l5beauty 的项目。然后我们在这个项目中使用 PHPUnit 进行单元测试。最后,我们创建了一个 Markdowner
服务类,这有两个目的 一是测试, 二是并在以后将 markdown 转换为 HTML 。
这是一个相当长的章节,因为测试是一个很大的话题,而一个章节无法给出公正的评价。但是,正如我所提到的,测试并不是本书的重点。在随后的章节中,将不再进行测试。
下一章我们讨论一些如何让系统更快的话题如何?