mockery 是個好東西,你可以輕易的 mock instance ,好讓你在寫 test 的時候不會捶心肝。但總是有些地方總是無法符合我的需求,所以才有了這個 package

mockery overload properties,這算是加強 mockery 的功能。因為之前在寫 test case 的時候,有時候會遇到像是下面的寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class User
{
public $id;
public $isVip;
public $rank;

/**
* construct
*/
public function __construct($id)
{
// some global static function
$dbh = DB_INSTANCE::getCurrent();
$sql = "
SELECT id, isVip, rank
FROM user
WHERE id = :id
";
$stm = $dbh->prepare($sql);
$bind = $stm->bindValue(':id', $id, PDO::PARAM_INT);
$stm->execute();
$user = $stm->fetch(PDO::FETCH_ASSOC);

$this->id = $user['id'];
$this->isVip = $user['isVip'];
$this->rank = $user['rank'];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Vip
{
/**
* check user is vip or not
*
* @return boolean is vip
*/
public function bonus($userId)
{
$defaultBonus = 100;
$user = new User($userId);

if ($user->isVip) {
$bonus = $defaultBonus * $user->rank + 55;
}
else {
$bonus = $defaultBonus * 0.5 + $user->rank;
}
return $bonus;
}
}

Vipfunction bonus 裡,使用 new User 的方式取得資料庫的資料,User__construct 的時候會把值設給 public properties,所以如果要用 Mockery 去 overload instance 的時候,會無法 mock User 的 properties,就會變成不得不在 test 的時候 insert 一筆資料進 user table,可以參考這個 test case

解決方式

如果不打算重構改由 DI 的方式來寫 test case 的話,可以參考這個 來寫 test case,這樣測試 Vip 的時候,即使是 User 是 hard dependencies,也可以簡單 mock。

如果想用簡單的 DI 來處理的話,在這 example code 裡就要改為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Vip
{
/**
* check user is vip or not
*
* @return boolean is vip
*/
public function bonus(User $user)
{
$defaultBonus = 100;
if ($user->isVip) {
$bonus = $defaultBonus * $user->rank + 55;
}
else {
$bonus = $defaultBonus * 0.5 + $user->rank;
}
return $bonus;
}
}

function bonus 傳入 User instance,而不是一個 $userId,這樣的話你就可以隨意的 mock User instance 了。