最近想学写单元测试,但是无从下手。有什么写的好的单元测试可以参考吗?
PHP
1
murmur 2016-06-09 17:55:14 +08:00
单元测试就是软件测试教程讲那些 正常值 边界值 异常值 不符合规定的输入 都来一套
不过话说回来有保证全周期覆盖单元测试的么来回一下。。军工的不算 |
2
iyaozhen 2016-06-09 18:23:52 +08:00 1
建议先看着 PHPUnit 的入门文档,讲得还比较清楚。
然后我再无耻的“ SEO ”之前在公司小组内分享的《 PHP 单元测试-mock 和数据库测试》 https://iyaozhen.com/php-unit-test-mock-and-db-test.html 个人感觉写单元测试和调试时自测差多不,主要是一些规范化的套路。 |
3
julyclyde 2016-06-09 20:24:45 +08:00
哈,很容易写着写着就发现自己写的模块太大了,得拆
|
4
fyibmsd 2016-06-09 20:31:09 +08:00 via Android
有一个项目, PHPDesignPattern 就是 php 各种设计模式的实现,有完整的测试用例,可以参考下
|
5
wujunze 2016-06-09 21:03:47 +08:00
PHPUnit +1
|
6
nonesuccess 2016-06-09 22:21:41 +08:00
带数据库或者外部资源的单元测试要怎么搞?
或者说,怎样才能把单元测试和数据库分开? |
7
murmur 2016-06-09 22:24:01 +08:00
@nonesuccess 数据库不建议 mockup 有些东西上了数据库才发现不对就不好玩了
还是用真实数据吧 junit 的话一般写一些代码在 before after 里捏造一些数据 测试完再把数据删掉 |
8
feiyuanqiu 2016-06-09 22:45:21 +08:00 1
@nonesuccess 一般来说,如果待测试的代码使用到了第三方的 api ,应该尽可能地避免在代码里直接依赖,尽量使用依赖注入,然后测试的时候注入一个 mock 的依赖对象,就可以很简单地进行测试了,比如有这样的代码:
class Sample { function sampleMethod() { $client = new ThirdpartyClient(); $client->doSomething(); ... } } 这种代码就非常难以测试,因为你的测试结果会依赖于这个真实的 ThirdpartyClient ,而它的行为是不可控制的,这个时候最好重构一下代码,将 ThirdpartyClient 作为方法的参数(更好的方式是作为这个类的构造方法的参数,然后用依赖注入容器去处理依赖的注入): class Sample { function sampleMethod(ThirdpartyClient $client) { $client->doSomething(); ... } } 这样,在写测试的时候,就可以注入一个模拟 ThirdpartyClient 行为的 Mock 对象( PHPUnit 的 getMockBuilder 方法可以很方便地进行这个工作),来进行测试工作。 你需要记住的就是单元测试的对象是当前这个代码单元的逻辑, ThirdpartyClient 的行为不在这个测试的范畴内,你只需要假设它始终会按照它的接口说明进行返回就可以了 对于数据库测试,我目前采用的方式是 PHPUnit 的 PHPUnit_Extensions_Database_TestCase 工具来做的 构造一个 fake DAO (这个 DAO 连接的是我的测试数据库),每个模块的测试会单独提供测试数据集, PHPUnit 在执行每个测试用例前,会调用 setUp 方法,这个方法会将测试数据集初始化到测试数据库,然后将这个测试 Dao 注入到待测试的模块,这个模块之后的所有数据库操作,都会通过这个测试 Dao 来执行,就保证了每次执行测试,数据的一致。 这里也有个简单的例子: class OrderServiceTest extends GenericDatabaseTestCase { /** * 订单模块实例 * * @var OrderService */ protected $service; /** * 初始化测试数据集 * * @return \PHPUnit_Extensions_Database_DataSet_CompositeDataSet * @throws \Exception */ public function getDataSet() { return $this->loadTestDataset( [ 'Order/orders', 'Order/order_items', ] ); } /** * 初始化基境 * * @return void * @throws \Exception */ public function setUp() { parent::setUp(); $this->service->setDao($this->getMockDao()); } |
9
feiyuanqiu 2016-06-09 22:52:48 +08:00 1
没写完,按错回复了...那就不多说了
大致的流程就是: 1 、准备测试数据集,测试数据集会填充到测试数据库里 2 、 setUp 方法会在执行每个测试用例前执行,在这里会将测试数据库的连接替换掉模块正常的数据库连接,保证每次执行测试案例的时候测试数据都是一致的,不影响其他环境的 3 、执行测试方法,因为待测试方法的数据库连接已经被我们的测试数据库连接劫持了,所以这个方法里面操作的所有数据库数据都是测试数据库中的数据 4 、执行 tearDown 方法 一般的单元测试看看官方文档就行了 另外,可以看看 google 测试工程师的这个文档 Guide: Writing Testable Code http://misko.hevery.com/code-reviewers-guide/ 单元测试对代码质量还是很有帮助的,而且写了几天之后就会对依赖注入非常痴迷... |
10
msg7086 2016-06-09 22:55:07 +08:00
其实做网站的话推荐你看看集成测试(也就是功能测试)。
|
11
codeek 2016-06-10 10:37:18 +08:00
单元测试最好的入门方式是 TDD (Test Driven Development),即测试驱动开发。简单来讲,就是先写测试,后写实现代码。
TDD 不是什么高深的概念,只是一种 Agile 的实践方式。很多人习惯先写实现代码,后“加测试”的编码方式,原因有二,一是实现代码还没有,不知道对哪个方法进行测试;二是懒,觉得测试是额外的工作负担,不到万不得已(比如:项目组强制)不会写测试,即使写也就挑几个好弄的方法,加一个正常流的测试。 这样的写法很容易导致几个问题: 1. 实现代码耦合,不便测试; 2. 实现代码过度设计,类文件剧增,代码量一多,维护性绝对不好; 3. 测试覆盖率很低,单元测试是拿来忽悠领导的,跟质量无关。 楼主说自己无从下手,估计是用了“加测试”的方式。如果是这样,那么 refactor (重构) 是你该先学的技能。设计良好的代码,接口很清晰,耦合度低(比如:楼上提到的 DI [依赖注入] ),一般写单元测试非常容易。 TDD 具体的实践方式,这里我暂按不表,网上的教程多如牛毛。其实它的核心理念就是让你知道如何从需求出发,拆接出任务( tasking ),按照任务一条条来写完测试。然后按照测试->实现->重构(红->绿->黄)的圈完善所有的功能。坚持这样,功能就从需求逐步演化成可维护的代码了。 |