これこれ、このライブラリ。
fzaninotto/Faker: Faker is a PHP library that generates fake data for you
ざっくり翻訳しつつ、日本語データでの使い方へ。
faker とは
faker は PHP のライブラリで、偽のデータを提供する。データベースの初期化や XML ドキュメントの生成、ストレステスト、本番データの匿名化、などに活用できる。faker は Perl の Data::Faker と Ruby の Faker に影響を受けている。
faker の使い方
インストール方法
Pakagist にて提供されているので composer を使ってインストールできる。
composer require fzaninotto/faker
基本的な使い方
$faker = \Faker\Factory::create();
// faker オブジェクトの生成
echo $faker->name;
// 名前。例えば 'Lucy Cechtelar'
echo $faker->address;
// 住所。例えば
// "426 Jordy Lodge
// Cartwrightshire, SC 88120-6700"
echo $faker->text;
// 文章…というよりは Lorem 。例えば。
// Dolores sit sint laboriosam dolorem culpa et autem. Beatae nam sunt fugit
// et sit et mollitia sed.
// Fuga deserunt tempora facere magni omnis. Omnis quia temporibus laudantium
// sit minima sint.
faker には嬉しいことに、日本語を含む色々な言語のダミーデータをサポートしている。ここでは日本語での扱いを試してみる。
// 引数に言語を指定すると利用してくれる
$faker = \Faker\Factory::create('ja_JP');
echo $faker->name;
// 例えば '大垣 直子'
echo $faker->address;
// 例えば '2636573 滋賀県浜田市西区笹田町渡辺1-1-10'
echo $faker->realText;
// text は Lorem なので realText がある
// 例えば 'オン燈と、もうすっかりがきの音にすきっと思いなやさで伝つたわ」「なんだ」カムパネルラはこち見ていたの...'
どういうプロパティが用意されているか
英語プロパティは README に全てあって、n - m の範囲で乱数とか、「Dr.」などの敬称を使えるとか、日時とか、まあなんか色々とあるのでそっちを見ると良さそう。
Faker/readme.md at master · fzaninotto/Faker
日本語のだけ、ソースとにらめっこしないと分からないところがあったので、取り上げる。(網羅できてないかも)
country | 国名 |
---|---|
prefecture | 都道府県 |
city | 市 |
ward | 区 |
streetAddress | 町以下 |
postcode | 郵便番号 |
secondaryAddress | マンション名 |
company | 会社名 |
userName | ユーザ名 |
domainName | ドメイン名 |
メールアドレス | |
name | 名前(姓 + 名) |
lastName | 姓 |
firstName | 名 |
firstNameMale | 名(男性) |
firstNameFeMale | 名(女性) |
kanaName | 名前カナ |
lastKanaName | 姓カナ |
firstKanaName | 名カナ |
firstKanaNameMale | 名カナ(男性) |
firstKanaNameFemale | 名カナ(女性) |
phoneNumber | 電話番号 |
realText | 日本語文章 |
ORMとの連携
faker は CakePHP や Laravel の ORM と連携することができる(!?) 試してみたところ、連携というよりは ORM を通して、実際に DB に値を格納してくれるっぽい。
例えば CakePHP3 で使うならこんなふうに。
$faker = \Faker\Factory::create('ja_JP');
$populator = new \Faker\ORM\CakePHP\Populator($faker);
$populator->addEntity('Users', 5, [
'name' => function() use ($faker) { return $faker->name; },
'prefecture' => function() use ($faker) { return $faker->prefecture; },
'created' => null,
'modified' => null,
]);
$inserted = $populator->execute();
$Users = \Cake\ORM\TableRegistry::get('Users');
debug($Users->find('all')->toList());
// 例としてこのような出力になる
// [
// (int) 0 => object(App\Model\Entity\User) {
// 'id' => (int) 1,
// 'name' => '津田 裕樹',
// 'prefecture' => '栃木県',
// ....
// 'created' => object(Cake\I18n\FrozenTime) {
// 'time' => '2017-11-20T13:47:32+09:00',
// 'timezone' => 'Asia/Tokyo',
// 'fixedNowTime' => false
// },
// 'modified' => object(Cake\I18n\FrozenTime) {
// 'time' => '2017-11-20T13:47:32+09:00',
// 'timezone' => 'Asia/Tokyo',
// 'fixedNowTime' => false
// },
// },
// ...
// ]
特に指定をしなかったカラムについては DB の形を見て、結構いい感じに適当に faker な値で埋めてくれる。指定はクロージャで、 faker を使ってランダムなデータを作ることも出来るし、固定値にすることもできる。また、例としては 1 エンティティしか作らなかったが、addEntity を複数呼んで、 まとめて execute することで複数のエンティティも作ることができる。
シード値を設定する
テストで使うときやっぱりランダムだとつらいよね、というときのためにシード値を設定することができる。
$faker = \Faker\Factory::create();
$faker->seed(1234);
echo $faker->name;
// 何度コードを流しても以下の順番で流れる
// 'Miss Lorna Dibbert'
// 'Litzy Emard'
// 'Odessa Collins'
// ...
ランダムに提供される部分はそのとおりなのだが、日付については引数を指定しないと now() な値が使われるのでシードは同じでも出力される値が変わってしまうので、なんでもいいが毎回同じ値を入れる必要がある。
$faker = \Faker\Factory::create();
$faker->seed(1234);
echo $faker->dateTime();
// ランダムになる
$faker = \Faker\Factory::create();
$faker->seed(1234);
echo $faker->dateTime('2017/01/01 12:00:00');
// 固定パターンになる
faker には乱数を提供する関数もあり、シード値を設定することで、こういったものたちも固定パターン化される。便利ちゃん。
$faker = \Faker\Factory::create();
$faker->seed(1234);
echo $faker->numberBetween(0,100);
// 固定パターンになる
// 81, 50, 62, 18, 56, ...
faker の内部、プロバイダの話
何の気無しに \Faker\Factory::create() を呼び出していたが、実は内部でこんなような処理をしている。
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\en_US\Person($faker));
$faker->addProvider(new \Faker\Provider\en_US\Address($faker));
$faker->addProvider(new \Faker\Provider\en_US\PhoneNumber($faker));
$faker->addProvider(new \Faker\Provider\en_US\Company($faker));
$faker->addProvider(new \Faker\Provider\Lorem($faker));
$faker->addProvider(new \Faker\Provider\Internet($faker));
※実際のコードはこのあたり。 Faker/Factory.php at master · fzaninotto/Faker
プロバイダの土台として \Faker\Provider\Base がいるので、これを継承した適当なクラスを作り、 public なメソッドを作って return すれば良い。そのプロバイダクラスを new して addProvider すれば使えるようになる。
class Book extends \Faker\Provider\Base
{
public function title($nbWords = 5)
{
$sentence = $this->generator->sentence($nbWords);
return substr($sentence, 0, strlen($sentence) - 1);
}
public function ISBN()
{
return $this->generator->ean13();
}
}
$faker->addProvider(new Book($faker));
echo $faker->ISBN;
faker を使ってみて
説明にもあるが、テストコードにおいて、いや、そこはなんでも良いんだけど、それっぽい住所や名前をよろしく入力して欲しい、みたいな時。あるいは、DBの初期化時に、多いに力を発揮しそうな感触。
テストコードとして利用する場合、値が毎回ランダムになって使い所がイマイチわかりにくい気もちょっとするが、それで詰まるときってそんなに無いのでは…。値が被って~~~うあああ!!!!みたいなことはあると思う。
話はそれるが最近見かけた property-based testing という方法にはめちゃんこ合っていると思う。
Property-Based Testing for Godly Tests
そもそも PHPUnit なら Data Provider の仕組みがあるし、あるいは CakePHP なら Fixture が、 Laravel には Factory の仕組みがそれぞれにある(きっと他のフレームワークにもあるんじゃないかな、調べてない。。)ので、そういうこともやりやすいと思う。 ちなみに Laravel には faker が既に含まれていて、 Factory の仕組みを使うとガンガンにテストデータを作ってくれる。スゴイ。
Database Testing - Laravel - The PHP Framework For Web Artisans
ORMとの連携はできるとはいえ、既に書いたとおり、各フレームワークがそういう仕組みを提供しているので、それとバッティングするなあという気持ちも。その中で使っていく、みたいなイメージなのかな。。ちょっとわからないや。。