欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

PHPUnit + Laravel单元测试常用技能

程序员文章站 2023-11-21 18:35:04
1. 数据供给器 用来提供参数和结果,使用 @dataprovider 标注来指定使用哪个数据供给器方法。例如检测app升级数据是否符合预期,addproviderappupdate...

1. 数据供给器

用来提供参数和结果,使用 @dataprovider 标注来指定使用哪个数据供给器方法。例如检测app升级数据是否符合预期,addproviderappupdatedata()提供测试的参数和结果。testappupdatedata()检测appupdatedata()返回的结果是否和给定的预期结果相等,即如果$appid='apple_3.3.2_117', $result=['status' => 0, 'isios' => false], 则$data中如果含有['status' => 0, 'isios' => false], 则断言成功。建议在数据提供器,逐个用字符串键名对其命名,这样在断言失败的时候将输出失败的名称,更容易定位问题

示例代码:

<?php
  namespace tests\unit;

  use app\services\clientservice;
  use tests\testcase;

  class clientservicetest extends testcase
  {
    /**
     * @dataprovider addproviderappupdatedata
     *
     * @param $appid
     * @param $result
     */
    public function testappupdatedata($appid, $result)
    {
      $data = (new clientservice($appid))->appupdatedata();

      $this->asserttrue(count(array_intersect_assoc($data, $result)) == count($result));
    }

    public function addproviderappupdatedata()
    {
      return [
        'null'         => [null, ['status' => 0, 'isios' => false, 'latest_version' => 'v']],
        'error app id'     => ['sdas123123', ['status' => 0, 'isios' => false, 'latest_version' => 'v']],
        'android force update' => ['bx7_3.3.5_120', ['status' => 0, 'isios' => false]],
        'ios force update'   => ['apple_3.3.2_117', ['status' => 1, 'isios' => true]],
        'android soft update' => ['sanxing_3.3.2_117', ['status' => 2, 'isios' => false]],
        'ios soft update'   => ['apple_3.3.3_118', ['status' => 2, 'isios' => true]],
        'android normal'    => ['fhqd_3.3.6_121', ['status' => 1, 'isios' => false]],
        'ios normal'      => ['apple_3.3.5_120', ['status' => 1, 'isios' => true]],
        'h5'          => ['h5_3.3.3', ['status' => 1, 'isios' => false]]
      ];
    }
  }

断言成功结果:

PHPUnit + Laravel单元测试常用技能

2. 断言方法

常用有asserttrue(), assertfalse(), assertnull(), assertequals(), assertthat()。

assertthat()自定义断言。常用的约束有isnull()、istrue()、isfalse()、isinstanceof();常用的组合约束logicalor()、logicaland()。例如检测返回的结果是否是null或apiapp类。

示例代码:

<?php
  namespace tests\unit;

  use app\models\apiapp;
  use app\services\systemconfigservice;
  use tests\testcase;

  class systemconfigservicetest extends testcase
  {
    /**
     * @dataprovider additionprovidergetlatestupdateappapi
     *
     * @param $apptype
     */
    public function testgetlatestupdateappapi($apptype)
    {
      $result = systemconfigservice::getlatestupdateappapi($apptype);
      $this->assertthat($result, $this->logicalor($this->isnull(), $this->isinstanceof(apiapp::class)));
    }

    public function additionprovidergetlatestupdateappapi()
    {
      return [
        'apple'  => [1],
        'android' => [2],
        'null'  => [9999]
      ];
    }
  }

断言成功结果:

PHPUnit + Laravel单元测试常用技能

3. 对异常进行测试

使用expectexceptioncode()对错误码进行检测,不建议对错误信息文案进行检测。例如检测设备被锁后是否抛出3026错误码。

示例代码:

<?php
  namespace tests\unit;

  use app\services\usersecurityservice;
  use illuminate\support\facades\cache;
  use tests\testcase;

  class usersecurityservicetest extends testcase
  {
    public static $userid = 4;

    /**
     * 设备锁检测
     * @throws \app\exceptions\userexception
     */
    public function testdevicechecklock()
    {
      $this->expectexceptioncode(3026);
      cache::put('device-login-error-account-', '1,2,3,4,5', 300);
      usersecurityservice::$request = null;
      usersecurityservice::$udid  = null;
      usersecurityservice::devicecheck(self::$userid);
    }
  }

断言成功结果:

PHPUnit + Laravel单元测试常用技能

4. 测试私有属性和私有方法使用反射机制

如果只测试私有方法可使用reflectionmethod()反射方法,使用setaccessible(true)设置方法可访问,并使用invokeargs()或invoke()调用方法(invokeargs将参数作为数组传递)。例如检测ip是否在白名单中。

示例代码:

被检测代码:

namespace app\facades\services;

  /**
   * class webdefender
   */
  class webdefenderservice extends baseservice
  {
     //ip白名单
    private $ipwhitelist = [
      '10.*', 
      '172.18.*', 
      '127.0.0.1' 
    ];

    /**
     * ip是否在白名单中
     *
     * @param string $ip
     *
     * @return bool
     */
    private function checkipwhitelist($ip)
    {
      if (!$this->ipwhitelist || !is_array($this->ipwhitelist)) {
        return false;
      }
      foreach ($this->ipwhitelist as $item) {
        if (preg_match("/{$item}/", $ip)) {
          return true;
        }
      }

      return false;
    }
   }

检测方法:

<?php

  namespace tests\unit;

  use app\facades\services\webdefenderservice;
  use tests\testcase;

  class webdefendertest extends testcase
  {
    /**
     * 测试ip白名单
     * @dataprovider additionproviderip
     *
     * @param $ip
     * @param $result
     *
     * @throws \reflectionexception
     */
    public function testipwhite($ip, $result)
    {
      $checkipwhitelist = new \reflectionmethod(webdefenderservice::class, 'checkipwhitelist');
      $checkipwhitelist->setaccessible(true);
      $this->assertequals($result, $checkipwhitelist->invokeargs(new webdefenderservice(), [$ip]));
    }

    public function additionproviderip()
    {
      return [
        '10 ip' => ['10.1.1.7', true],
        '172 ip' => ['172.18.2.5', true],
        '127 ip' => ['127.0.0.1', true],
        '192 ip' => ['192.168.0.1', false]
      ];
    }
   }

测试私有属性可使用reflectionclass(), 获取属性用getproperty(), 设置属性的值用setvalue(), 获取方法用getmethod(), 设置属性和方法可被访问使用setaccessible(true)。例如检测白名单路径。

示例代码:

被检测代码:

<?php
  namespace app\facades\services;

  use app\exceptions\exceptioncode;
  use app\exceptions\userexception;
  use illuminate\support\facades\cache;

  /**
   * cc攻击防御器
   * class webdefender
   */
  class webdefenderservice extends baseservice
  {
    //路径白名单(正则)
    private $pathwhitelist = [
      //'^auth\/(.*)',
    ];

    private static $request = null;

     /**
     * 请求路径是否在白名单中
     *
     * @return bool
     */
    private function checkpathwhitelist()
    {
      $path = ltrim(self::$request->getpathinfo(), '/');
      if (!$path || !$this->pathwhitelist || !is_array($this->pathwhitelist)) {
        return false;
      }
      foreach ($this->pathwhitelist as $item) {
        if (preg_match("/$item/", $path)) {
          return true;
        }
      }

      return false;
    }
  }

检测方法:

<?php
  namespace tests\unit;

  use app\facades\services\webdefenderservice;
  use illuminate\http\request;
  use tests\testcase;

  class webdefendertest extends testcase
  {
     /**
     * 检测白名单路径
     * @dataprovider additionproviderpathwhitelist
     *
     * @param $pathproperty
     * @param $request
     * @param $result
     *
     * @throws \reflectionexception
     */
    public function testcheckpathwhitelist($pathproperty, $request, $result)
    {
      $reflectedclass = new \reflectionclass('app\facades\services\webdefenderservice');

      $webdefenderservice   = new webdefenderservice();
      $reflectedpathwhitelist = $reflectedclass->getproperty('pathwhitelist');
      $reflectedpathwhitelist->setaccessible(true);
      $reflectedpathwhitelist->setvalue($webdefenderservice, $pathproperty);

      $reflectedrequest = $reflectedclass->getproperty('request');
      $reflectedrequest->setaccessible(true);
      $reflectedrequest->setvalue($request);

      $reflectedmethod = $reflectedclass->getmethod('checkpathwhitelist');
      $reflectedmethod->setaccessible(true);
      $this->assertequals($result, $reflectedmethod->invoke($webdefenderservice));
    }

    public function additionproviderpathwhitelist()
    {
      $allpath      = ['.*'];
      $checkpath     = ['^auth\/(.*)'];
      $authsendsmsrequest = new request([], [], [], [], [], ['http_host' => 'api.dev.com', 'request_uri' => '/auth/sendsms']);
      $indexrequest    = new request([], [], [], [], [], ['http_host' => 'api.dev.com', 'request_uri' => '/']);
      $nomatchrequest   = new request([], [], [], [], [], ['http_host' => 'api.dev.com', 'request_uri' => '/product/sendsms']);

      return [
        'index'        => [[], $authsendsmsrequest, false],
        'no request'     => [$allpath, $indexrequest, false],
        'all request'     => [$allpath, $authsendsmsrequest, true],
        'check auth sms'   => [$checkpath, $authsendsmsrequest, true],
        'check path no match' => [$checkpath, $nomatchrequest, false]
      ];
    }
  }

5. 代码覆盖率

使用--coverage-html导出的报告含有类与特质覆盖率、行覆盖率、函数与方法覆盖率。可查看当前单元测试覆盖的范围。例如输出webdefendertest的代码覆盖率到桌面(phpunit tests/unit/webdefendertest --coverage-html ~/desktop/test)

PHPUnit + Laravel单元测试常用技能

6. 指定代码覆盖率报告要包含哪些文件

在配置文件(phpunit.xml)里设置whitelist中的processuncoveredfilesfromwhitelist=true, 设置目录用<directory>标签,设置文件用<file>标签。例如指定app/services目录下的所有文件和app/facades/services/webdefenderservice.php在报告中。

示例代码:

 <?xml version="1.0" encoding="utf-8"?>
  <phpunit backupglobals="false"
       backupstaticattributes="false"
       bootstrap="tests/bootstrap.php"
       colors="true"
       converterrorstoexceptions="true"
       convertnoticestoexceptions="true"
       convertwarningstoexceptions="true"
       processisolation="false"
       stoponfailure="false">
    <testsuites>
      <testsuite name="unit">
        <directory suffix="test.php">./tests/unit</directory>
      </testsuite>

      <testsuite name="feature">
        <directory suffix="test.php">./tests/feature</directory>
      </testsuite>
    </testsuites>
    <filter>
      <whitelist processuncoveredfilesfromwhitelist="true">
        <directory suffix=".php">./app/services</directory>
        <file>./app/facades/services/webdefenderservice.php</file>
      </whitelist>
    </filter>
    <php>
      <server name="app_env" value="local"/>
      <server name="bcrypt_rounds" value="4"/>
      <server name="cache_driver" value="credis"/>
      <server name="mail_driver" value="array"/>
      <server name="queue_connection" value="sync"/>
      <server name="session_driver" value="array"/>
      <server name="app_config_cache" value="bootstrap/cache/config.phpunit.php"/>
      <server name="app_services_cache" value="bootstrap/cache/services.phpunit.php"/>
      <server name="app_packages_cache" value="bootstrap/cache/packages.phpunit.php"/>
      <server name="app_routes_cache" value="bootstrap/cache/routes.phpunit.php"/>
      <server name="app_events_cache" value="bootstrap/cache/events.phpunit.php"/>
    </php>
  </phpunit>

7. 参考文档

phpunit官方文档 https://phpunit.readthedocs.io/zh_cn/latest/index.html
反射类
反射方法

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。