dongxu7408 2018-04-04 11:18
浏览 67

PHP - 在其主体中使用unmockable类的单元测试方法(PHPUnit)

I have a method that uses another class to calculate its outcome, that I want to test using PHPUnit.

 * Returns true if the given user has been granted the given permission.
 * @param User $user
 * @param AbstractPermission $permission
 * @return bool
public function userPermissionGranted(User $user, AbstractPermission $permission) : bool
    // Retrieve model from database.
    $user_permission = UserPermission::scopeUser($user)

    return $user_permission ? $user_permission->isGranted() : $permission->isGrantedByDefault();

Leaving out of consideration what this method actually does, I am wondering how to test this method. I can pass mocks of the User and AbstractPermission classes to the method, but the UserPermission class that is used inside the method's body (to retrieve a model from the database) I can do nothing with.

On top of that, if I pass mocks of the User and Permisson classes, they won't exist in the database, so when UserPermission queries the database, it will receive no results and the method will fail.

What do I do here? Is it considered good practice to simply mock the database (i.e. copying the live db structure and filling it with test data) and let my model query that database, and just trusting that everything is OK? Any suggestions on what to do here?

On a side note, UserPermission is an Eloquent model. I am merely making use of Eloquent here - without Laravel.

  • 写回答

2条回答 默认 最新

  • doubingqi5829 2018-04-04 14:39

    As a general rule, you can't directly mock static methods - at least, there's no good way to do it. Depending on how your application is set up, you might be able to hack something together that involves redefining the method with runkit or perhaps messing with includes/autoloader to load a mock class instead of the real one, but such solutions are kludgey at best.

    One simple approach to allow unit-testing would be to wrap your static method calls in an instance method. So you'd create a new class with instance methods that call the static methods. Of course, you wouldn't be able to test that new class, but if it's a thin wrapper around the static methods then there's not really any value in testing it anyway.

    So you might end up with something like this, for example:

    class UserPermissionWrapper {
        public function getUserPermission($user) {
            return UserPermission::scopeUser($user);

    Then you can inject that into your original class and get something like this:

    public function userPermissionGranted(User $user, AbstractPermission $permission) : bool
        // Assume this is an instance of UserPermissionWrapper injected at construction
        $user_permission = $this->userPermissionWrapper
        return $user_permission ? $user_permission->isGranted() : $permission->isGrantedByDefault();

    Now you have an object calling instance methods, so you can inject a mock version of that class and set up the method calls in the normal way.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?



  • ¥50 关于多次提交POST数据后,无法获取到POST数据参数的问题
  • ¥15 win10,这种情况怎么办
  • ¥15 如何在配置使用Prettier的VSCode中通过Better Align插件来对齐等式?(相关搜索:格式化)
  • ¥100 在连接内网VPN时,如何同时保持互联网连接
  • ¥15 MATLAB中使用parfor,矩阵Removal的有效索引在parfor循环中受限制
  • ¥20 Win 10 LTSC 1809版本如何无损提升到20H1版本
  • ¥50 win10 LTSC 虚拟键盘不弹出
  • ¥30 微信小程序请求失败,网页能正常带锁访问
  • ¥30 德飞莱51单片机实现C4炸弹
  • ¥50 CrossLink-LIF-MD6000 型 FPGA 的 CMOS 转 MIPI D-PHY IP 核功能使用异常