79
let's database testing!! 2010/10/16 YAPC::Asia 2010 @xaicron

Let s database_testing

Embed Size (px)

Citation preview

Page 1: Let s database_testing

let's database testing!!

2010/10/16 YAPC::Asia 2010@xaicron

Page 2: Let s database_testing

自己紹介

name: Yuji Shimada ( 嶋田 裕二 )

works: DeNAtwitter: @xaicronblog: http://blog.livedoor.jp/xaicron/

Page 3: Let s database_testing

皆さん

Page 4: Let s database_testing

データーベース処理のテスト

Page 5: Let s database_testing

してますか?

Page 6: Let s database_testing

DB のテストをすると

この SELECT なんにもマッチしない...INSERT したデータが文字化けしてるやん!UPDATE や DELETE に WHERE 付け忘れてて全件あばばば TRIGGER が間違ってて、データ壊れた。。。

Page 7: Let s database_testing

という事態を未然に防げます!

Page 8: Let s database_testing

というか。。。

Page 9: Let s database_testing

永続的なストレージに実際に入出力があるんだから

テストするのはあたりまえですね!

Page 10: Let s database_testing

今回は MySQL に焦点を絞ってお話します

Page 11: Let s database_testing

ORM の話もしません

Page 12: Let s database_testing

基本的には 生DBI をつかってやります

Page 13: Let s database_testing

Haw To Testing

Page 14: Let s database_testing

鉄則

Page 15: Let s database_testing

ローカルでテストできる環境を作ること!!

Page 16: Let s database_testing

よくない例

ステージング環境のDBに接続してテストする本番は MySQL だけど、テストでは SQLite を使う自分のローカルに立ち上がっている MySQL  をテストに使う

 

Page 17: Let s database_testing

実際に存在している DB を使ってテストすると、毎回データが変わってテストにならない本番は MySQL なのに テストでは SQLite とかは意味がない

Page 18: Let s database_testing

Test の大まかな流れ

Test::mysqld を使って毎回クリーンな mysql を立ち上げるTest::Fixture::DBI を使ってテストデータを突っ込むDB 接続して、実際にデータの入出力が行われているかテストそして伝説へ...

Page 19: Let s database_testing

Test::mysqld

new するだけで、lcoal に 新規 mysql が立ち上がるDESTROY が走ったら、自動的に mysql を落とすAuthor: kazuhooku

Page 20: Let s database_testing

DEMO

Page 21: Let s database_testing

Test::Fixture::DBI

table 作ったり、 procedure / function / trigger 作ったり、data ぶっこんだりできる既存の DB から schema 定義をもってきたり、指定のレコードをとって来たりできる

make_*_yaml.pl で fixture や database 定義を YAML に吐き出せる

Author: ZIGOROu, (test: xaicron)

Page 22: Let s database_testing

DEMO

Page 23: Let s database_testing

なかなか便利ですが

Page 24: Let s database_testing

テスト毎にこれを毎回書くのはめんどいので

Page 25: Let s database_testing

適当に組み合わせたTest Module を書いて使うと

いいでしょう

Page 26: Let s database_testing

Test::Foo::Fixture::DBIみたいな名前にして

setup_

Page 27: Let s database_testing

実際のテストの書き方

Page 28: Let s database_testing

の前に

Page 29: Let s database_testing

DBI を直接扱う上で便利なモジュールを紹介します

Page 30: Let s database_testing

DBI と親和性の高いモジュール

SQL::AbstractDBIx::DBHResolverDBIx::Connector

Page 31: Let s database_testing

DBIx::Connector ->DBIx::DBHResolver ->

DBI

Page 32: Let s database_testing

という感じでラップするとなかなかいい感じ

Page 33: Let s database_testing

DBIx::Connector

DBI の超薄いラッパー

$dbh を局所化できる多分最大の利点

fixup => で再接続トランザクション処理をよしなにやってくれる

Page 34: Let s database_testing

DBIx::DBHResolver

DBI 専用の container みたいなノリ複数の DB に接続するのに便利

Master / SlaveUser と Diary が別の DB

Sharding していてもかなり直感的

DBIx::Connector とも協調しやすい

Page 35: Let s database_testing

+SQL::Abstract

Page 36: Let s database_testing

my $row = $model->connector('SLAVE')->txn(fixup => sub {        my $dbh = shift;        my ($stmt, @bind) = $model->sql->select(...);        return $dbh->selectrow_hashref($stmt, undef, @bind);});

Page 37: Let s database_testing

みたないな感じでつかえる

Page 38: Let s database_testing

ようやく実際のテストの話

Page 39: Let s database_testing

SELECT した結果を返すもののテスト

Page 40: Let s database_testing

my $row = try {    $self->connector('SLAVE', $user_id)->run(fixup => sub {        my $dbh = shift;        my ($stmt, @bind) = $self->sql->select(            'user_data',            [qw/id name/],            { id => $user_id, disabled => 0 },          );          $dbh->selectrow_hashref($stmt, undef, @bind);    });}catch {    my $e = $_;    ...};

Page 41: Let s database_testing

mysqld に fixture を入れる実際にたたいて値を取得取得した値が正しいかチェック

Page 42: Let s database_testing

DEMO

Page 43: Let s database_testing

INSERT のテスト

Page 44: Let s database_testing

try {    my $seq = $self->next_seq('MASTER', 'seq_tweet');    $self->connector('MASTER', $user_id)->run(fixup => sub {        my $dbh = shift;        my ($stmt, @bind) = $self->sql->insert(            'tweet',            { id => $seq, ... },        );        $dbh->do($stmt, undef, @bind);        $dbh->commit;    });}catch {    my $e = $_;};

Page 45: Let s database_testing

実際にたたいて値を入力正常に INSERT されているかチェック

Page 46: Let s database_testing

UPDATE とか DELETE も同じ感じ

Page 47: Let s database_testing

特別に難しいことはやっていない

Page 48: Let s database_testing

すべての SQL に対して地道にテストを書きましょう

Page 49: Let s database_testing

PROCEDURE / FUNCTION/ TRIGGER

のテスト

Page 50: Let s database_testing

FUNCTION とは

そのまんま一連のSQLを関数化できるCALL function_name(args) で呼べる値を返すので、参照系に使える

Page 51: Let s database_testing

PROCEDURE と

値を返さない FUNCTION主に更新用途 CALL procedure_name(args) で呼べる

Page 52: Let s database_testing

TRIGGER とは

テーブルの更新タイミングで発火INSERT / UPDATE / DELETE

SQL なら何でも書ける

PROCEDURE をよく使う (更新なので)

Page 53: Let s database_testing

FUNCTION のテスト

Page 54: Let s database_testing

SELECT とおんなじ

Page 55: Let s database_testing

PROCEDURE / TRIGGERのテスト

Page 56: Let s database_testing

INSERT / UPDATE /DELETEとおんなじ

Page 57: Let s database_testing

Test::mysqld の問題点

Page 58: Let s database_testing

make test したときにすっごい時間がかかる orz

Page 59: Let s database_testing

1テスト毎に mysqld を立ち上げて落としてるから仕方ないよね

Page 60: Let s database_testing

なんとかしたい

Page 61: Let s database_testing

なんとかしたい

Page 62: Let s database_testing
Page 63: Let s database_testing

Makefile の書き換え大変です!

Page 64: Let s database_testing

Module かいたよーModule::Install::ExtendsMakeTest

Page 65: Let s database_testing

replace_default_make_test(    includes         => ['t/lib'],    modules          => ['Test::MyApp::DB'],    before_run_codes => [        sub {            $SIG{INT} = sub { CORE::exit 1 };            my $db = Test::MyApp::DB->setup;            $ENV{TEST_MY_SOCKET} = $db->my_cnf->{socket};        },      ],  );

Page 66: Let s database_testing

みたいな感じで書くとmake test 時に mysqld

が立ち上がる

Page 67: Let s database_testing

あとは、socket ファイルとかを指定して接続すれば OK

Page 68: Let s database_testing

$ENV に値があったら立ち上がっている mysqld を使って、

なかったら 新規に立ち上げるようにしておけばいい

Page 69: Let s database_testing

DEMO

Page 70: Let s database_testing

というわけで

Page 71: Let s database_testing

プログラムから扱いそうなDB の処理は

Page 72: Let s database_testing

ちゃんとテストできる!!

Page 73: Let s database_testing

ので、テストを書きましょう

Page 74: Let s database_testing

自分(とみんな)のために

Page 75: Let s database_testing

まとめ

地道にやっていけば DB 処理の大部分をテスト結構ツール類が充実してきているでもまだ自分で書く部分が多い印象割とこの辺のノウハウがたまりつつあるので、よくある処理は Module 化したい

Page 76: Let s database_testing

DEMO で使った適当なプロダクト

http://github.com/xaicron/Mayoi

Page 77: Let s database_testing

ご清聴ありがとうございました

Page 78: Let s database_testing

Questions?

Page 79: Let s database_testing

閑話休題