撰寫單元測試
預期測試時會遇到 error
$this->expectWarning();
// Warning: Division by zero
$a = 2 / 0;
Slide 51
Slide 51 text
撰寫單元測試
Fixtures
建立待測對象 (data structure / object) 並初始化
測試對象需要在每個 test case 執行前 reset
測試開始前給定測試用資料
降低不同 test case 互相影響 ( 依賴 ) 產生的問題
Slide 52
Slide 52 text
撰寫單元測試
public function setUp() {
$this->stack = new SplStack();
$this->stack->push(1);
$this->stack->push(2);
}
public function tearDown() {
$this->stack = null;
}
Slide 53
Slide 53 text
撰寫單元測試
public function setUp() {
$this->stack = new SplStack();
$this->stack->push(1);
$this->stack->push(2);
}
public function testStack() {
$this->stack->count(); // int(2)
}
Slide 54
Slide 54 text
撰寫單元測試
public function test1() {
$this->stack->push('str');
$this->stack->count(); // int(3)
}
public fcuntion test2() {
$this->stack->count(); // int(2)
}
---- $this->stack 會在 test case 執行完成以後被 reset -----
Slide 55
Slide 55 text
撰寫單元測試
會用到 fixture 的情形
受測對象中不同的狀態 (state)
例如: NEW, DRAFT, PUBLISHED
Test case 執行後會導致受測對象狀態異動
受測對象需要初始化
或是要事先給定資料
Test Doubles
功能越簡單越好
不要為了 test doubles 再寫 unit test
class FakeMemcacahe {
function get($key) {};
function set($key, $val) {};
function keys() {};
.....
}
Slide 66
Slide 66 text
No content
Slide 67
Slide 67 text
單元測試 (Unit Test)
並非所有的 code 都有辦法建立 test case
Unit test 通過只能表示各項小功能正確
無法代表整個專案可以正確執行
靠整合測試來檢查整體行為是否如預期
SOLID Principles in OOP
Single responsibility
Open-closed
Liskov Substitution
Interface Segregation
Dependency Inversion
Slide 81
Slide 81 text
Single responsibility
單一職責原則
物件指處理單一類型的工作
高內聚
Slide 82
Slide 82 text
Single responsibility
Class User {
function getUserById();
function getUserByName();
function createUser();
}
僅處理 user
Slide 83
Slide 83 text
Single responsibility
Class User {
function getUserById();
function getUserByName();
function createUser();
function getUserByOrderId();
}
歪了
可能要建立 stub
Open-closed
class User {
protected $users = [];
}
class UserList extends User
implements Traversable {
protected $currentIndex = 0;
function current();
function next();
....
Slide 86
Slide 86 text
Open-closed
class TaskQueue {
public $tasks;
function pop();
function push();
}
$queue->$task[] = new Task();
Slide 87
Slide 87 text
Open-closed
class TaskQueue {
public $tasks;
function pop();
function push();
}
$queue->$task[] = new Task();
違反封閉原則
Slide 88
Slide 88 text
Open-closed
class TaskQueue {
private $tasks;
function pop();
function push();
}
$queue->push(new Task());
透過 interface 操作
Liskov Substitution
class Animal {}
class Food {}
class DogFood extends Food {}
class CatFood extends Food {}
Slide 91
Slide 91 text
Liskov Substitution
class Dog extends Animal {
public function eat($food) {
if (!$food instanceof DogFood) {
throw new
DogFoodException();
}
class Cat extends Animal {
public function eat(Food $food) {
... throw new CatFoodException();
Liskov Substitution
class Animal {
public function eat (Food $food) {}
}
class Dog extends Animal {
public function eat($food) {
if (!$food instanceof DogFood) {
parent::eat();
}
}
Interface Segregation
interface DogInterface() {
function walk();
function sleep();
}
Slide 98
Slide 98 text
Interface Segregation
class Dog implements DogInterface {
public function walk() {
return 'walking ...';
}
public function sleep() {
return '.. zz ZZ';
}
}
Slide 99
Slide 99 text
Interface Segregation
class RobotDog implements DogInterface {
public function walk() {
return 'walking ...';
}
public function sleep() {
throw new Exception();
}
}
Slide 100
Slide 100 text
Interface Segregation
interface walkable () {
function walk();
}
interface sleepable () {
function sleep();
}
Slide 101
Slide 101 text
Interface Segregation
class Dog implements walkable,
sleepable {
}
class RobotDog implements walkable {
}
Dependency Inversion
class User {
function getByFile(File $reader) {}
function getByAws(AwsS3 $reader) {}
function getByDb(MySQL $reader) {}
}
Slide 104
Slide 104 text
Dependency Inversion
class User {
function getByFile(File $reader) {}
function getByAws(AwsS3 $reader) {}
function getByDb(MySQL $reader) {}
}
User 類別需要知道各種 reader 的實作方
式
Slide 105
Slide 105 text
Dependency Inversion
User File
AWS S3
MySQL
User 與 3 個類別相依
Slide 106
Slide 106 text
Dependency Inversion
Interface ReaderInterface {
function open();
function read();
function save();
}
class File implements ReaderInterface;
class AwsS3 implements ReaderInterface;
class MySQL implements ReaderInterface;
Slide 107
Slide 107 text
Dependency Inversion
class User {
function get(ReaderInterface $reader){
$reader->open();
$users = $reader->read();
}
}
Slide 108
Slide 108 text
Dependency Inversion
User File
AWS S3
MySQL
ReaderInterface
每個類別只和 1 個 interface 有相依關係