如何写单元测试 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
corboy
V2EX    程序员

如何写单元测试

  •  
  •   corboy 2016 年 6 月 9 日 4005 次点击
    这是一个创建于 3575 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近想学写单元测试,但是无从下手。有什么写的好的单元测试可以参考吗?

    PHP

    11 条回复    2016-06-10 10:37:18 +08:00
    murmur
        1
    murmur  
       2016 年 6 月 9 日
    单元测试就是软件测试教程讲那些 正常值 边界值 异常值 不符合规定的输入 都来一套
    不过话说回来有保证全周期覆盖单元测试的么来回一下。。军工的不算
    iyaozhen
        2
    iyaozhen  
       2016 年 6 月 9 日   1
    建议先看着 PHPUnit 的入门文档,讲得还比较清楚。

    然后我再无耻的“ SEO ”之前在公司小组内分享的《 PHP 单元测试-mock 和数据库测试》 https://iyaozhen.com/php-unit-test-mock-and-db-test.html

    个人感觉写单元测试和调试时自测差多不,主要是一些规范化的套路。
    julyclyde
        3
    julyclyde  
       2016 年 6 月 9 日
    哈,很容易写着写着就发现自己写的模块太大了,得拆
    fyibmsd
        4
    fyibmsd  
       2016 年 6 月 9 日 via Android
    有一个项目, PHPDesignPattern 就是 php 各种设计模式的实现,有完整的测试用例,可以参考下
    wujunze
        5
    wujunze  
       2016 年 6 月 9 日
    PHPUnit +1
    nonesuccess
        6
    nonesuccess  
       2016 年 6 月 9 日
    带数据库或者外部资源的单元测试要怎么搞?

    或者说,怎样才能把单元测试和数据库分开?
    murmur
        7
    mrmur  
       2016 年 6 月 9 日
    @nonesuccess 数据库不建议 mockup 有些东西上了数据库才发现不对就不好玩了
    还是用真实数据吧 junit 的话一般写一些代码在 before after 里捏造一些数据 测试完再把数据删掉
    feiyuanqiu
        8
    feiyuanqiu  
       2016 年 6 月 9 日   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());
    }
    feiyuanqiu
        9
    feiyuanqiu  
       2016 年 6 月 9 日   1
    没写完,按错回复了...那就不多说了
    大致的流程就是:
    1 、准备测试数据集,测试数据集会填充到测试数据库里
    2 、 setUp 方法会在执行每个测试用例前执行,在这里会将测试数据库的连接替换掉模块正常的数据库连接,保证每次执行测试案例的时候测试数据都是一致的,不影响其他环境的
    3 、执行测试方法,因为待测试方法的数据库连接已经被我们的测试数据库连接劫持了,所以这个方法里面操作的所有数据库数据都是测试数据库中的数据
    4 、执行 tearDown 方法

    一般的单元测试看看官方文档就行了

    另外,可以看看 google 测试工程师的这个文档 Guide: Writing Testable Code http://misko.hevery.com/code-reviewers-guide/

    单元测试对代码质量还是很有帮助的,而且写了几天之后就会对依赖注入非常痴迷...
    msg7086
        10
    msg7086  
       2016 年 6 月 9 日
    其实做网站的话推荐你看看集成测试(也就是功能测试)。
    codeek
        11
    codeek  
       2016 年 6 月 10 日
    单元测试最好的入门方式是 TDD (Test Driven Development),即测试驱动开发。简单来讲,就是先写测试,后写实现代码。

    TDD 不是什么高深的概念,只是一种 Agile 的实践方式。很多人习惯先写实现代码,后“加测试”的编码方式,原因有二,一是实现代码还没有,不知道对哪个方法进行测试;二是懒,觉得测试是额外的工作负担,不到万不得已(比如:项目组强制)不会写测试,即使写也就挑几个好弄的方法,加一个正常流的测试。

    这样的写法很容易导致几个问题:
    1. 实现代码耦合,不便测试;
    2. 实现代码过度设计,类文件剧增,代码量一多,维护性绝对不好;
    3. 测试覆盖率很低,单元测试是拿来忽悠领导的,跟质量无关。

    楼主说自己无从下手,估计是用了“加测试”的方式。如果是这样,那么 refactor (重构) 是你该先学的技能。设计良好的代码,接口很清晰,耦合度低(比如:楼上提到的 DI [依赖注入] ),一般写单元测试非常容易。

    TDD 具体的实践方式,这里我暂按不表,网上的教程多如牛毛。其实它的核心理念就是让你知道如何从需求出发,拆接出任务( tasking ),按照任务一条条来写完测试。然后按照测试->实现->重构(红->绿->黄)的圈完善所有的功能。坚持这样,功能就从需求逐步演化成可维护的代码了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5292 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 39ms UTC 05:44 PVG 13:44 LAX 22:44 JFK 01:44
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86