
はじめに
こんにちは!
新卒2年目(そろそろ言い訳できなくなってくる時期)のエンジニアです。
コードレビューでいろんなご指摘を頂くのですが、
その中でも特に指摘されたのが、、
• 「DIに従って」
• 「これだと単体テスト書けないよ!」
でした。
いわゆるテスタブルなコードにしようって話ですね。
DI…??単体テスト…???
っていう混乱状態から始まった自分が、ちょっとだけわかるようになってきたのでまとめてみました。
お手柔らかに….
テスタブルなコードってなんだ?
テストが書きやすいコード! です。
なるほど….(????)
自分は調べてこうなりました(笑)
実際の例を見てみましょうー!
テストしにくいコード(やりがち)
class UserService {
public function register(string $email) {
$user_dao = new UserDao(); // ← ここでnewしてる
$user_dao->save($email);
}
}
学生の時はひたすらこれでした笑
一見だいじょぶそう。でも問題が!!
- 依存
→UserServiceがUserDaoの実装に依存してしまっているので、単体テストができない - DB接続してしまう
→実際にDB接続してしまうため、単体テストができない
ノットテスタブルですね。
じゃあ、テスタブルなコードにするには?
キーワードは DI(Dependency Injection) です。
なんか必殺技みたいで強そう()
簡単に説明すると、
「newは外でやって、それを渡す」
それだけ!それだけ!
DIを考慮して書き直してみると、、
class UserService {
private $user_dao;
public function __construct(UserDao $user_dao) {
$this->user_dao = $user_dao;
}
public function register(string $email) {
$this->user_dao->save($email);
}
}
これでテスタブルなコード、単体テストが書けるようになりました!!!!
(…….)
なんでこれだと単体テストが書けるようになるんだ??
当時の自分はここでもピンと来ませんでした。
もう少し詳しく話します。
上記のようなコードにすることで、UserService は UserDao を直接インスタンス化する必要がなくなり、代わりに UserDao 型のもの(モック)を外部から渡すことができるようになりました!
なので単体テストが書けるように!!
いや待て、モックってなんだよ…
簡単に言うと、
単体テストで使う、指定したクラスを抽象化した偽物のこと!
実際にモックを使って単体テストを書いてみると…
//Mockery使用
// モックを作成
$mock_user_dao = Mockery::mock('UserDao');
// 期待する呼び出し内容を指定
$mock_user_dao->shouldReceive('save')
->once()
->with('test@example.com');
// テスト対象のサービスを生成
$service = new UserService($mock_user_dao);
// メソッドを実行
$service->register('test@example.com');
処理の説明は割愛しますが、
上記のようにUserDaoの偽物を実際に単体テストをしたいUserServiceに渡してあげることで、他に依存しない、UserServiceだけの単体テストができるというわけです。
単体テストをするメリット
1.コードを個別にテストすることで、バグの早期発見ができる!
2.リファクタリングした際、既存の処理が壊れていないことを担保できる!
3.手動テストでの確認漏れを防げる!
すごい、これがテスタブルってやつか…
まとめ
「テスタブルなコード」って、最初はふわっとしててよく分からない言葉でしたが、
実はとてもシンプルでした!
• newは外でやって渡す(=DI)
• モックを使えば、依存先を偽物に置き換えてテストできる
→ だから単体テストが書ける!
単体テストが書けるコードにすると、
バグを早く見つけられるし、リファクタも安心してできるし、何より怖くなくなる!
これが少しずつ実感できてきたなと思います。
コードレビューの指摘も少しずつ減ってきた……気がします!
これからもがんばります!
Views: 0