Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CakePHP Fixture Factories の登場によって変化する、PHPプロジェクトにおけるテストフィクスチャ管理の選択肢 / Test Fixture Management in PHP Project

CakePHP Fixture Factories の登場によって変化する、PHPプロジェクトにおけるテストフィクスチャ管理の選択肢 / Test Fixture Management in PHP Project

Takuya Obara (barbara)

March 31, 2022
Tweet

More Decks by Takuya Obara (barbara)

Other Decks in Programming

Transcript

  1. CakePHP Fixture Factories の登場によって変化する、 PHPプロジェクトにおけるテストフィクスチャ管理の 選択肢 2022/04/11 PHPerKaigi 2022 #phperkaigi

    #b @takoba
  2. 誰? takoba / ばーばら 🌹 🏢 コネヒト株式会社 󰳕 Webアプリケーション /

    PHP / ちょっとReact / たまにUXデザイン 🏓 インターネット / 音楽 / カレーライス / 銭湯 / Jリーグ / ポッドキャスト @takoba @takoba_
  3. [PR]会社紹介 「あなたの家族像が実現できる社会をつくる」というビジョンを掲げ、 ママの3人に1人(※) が利用する「ママリ」などを運営しています。 ※「ママリ」で2019年に出産予定と設定したユーザー数と、厚生労働省発表「人口動態統計」の出生数から算出

  4. [PR]会社紹介 「あなたの家族像が実現できる社会をつくる」というビジョンを掲げ、 ママの3人に1人(※) が利用する「ママリ」などを運営しています。 ※「ママリ」で2019年に出産予定と設定したユーザー数と、厚生労働省発表「人口動態統計」の出生数から算出 「ママリ」は主にサーバーサイドが CakePHP 3-4 で書かれています! (ご興味のある方は

    Twitter: @takoba_ まで 💌 )
  5. xUnitとテストフィクスチャと私 (愛するあなたのために)前提

  6. • cf. xUnit - Wikipedia ◦ > コンピュータプログラムの単体テスト(ユニットテスト)を行うためのテ スティングフレームワークの総称 ◦

    この中の xUnitの設計 項の中に"テストスイート"、"テストの実行"、"ア サーション(表明、検証)"と並んで"テストフィクスチャ"がある • 各種言語にxUnitに準拠したテスティングフレームワークが存在する ◦ JUnit(Java向けテスティングフレームワーク) ◦ test-unit / Test::Unit(Ruby向けテスティングフレームワーク) ◦ MochiKit(JavaScript向けテスティングフレームワーク) ◦ PHPUnit(PHP向けテスティングフレームワーク) (おさらい)xUnitとは?
  7. • cf. xUnit - Wikipedia ◦ > テストを実行、成功させるために必要な状態や前提条件の集合を、フィク スチャ(英語版)と呼ぶ。これらはテストコンテキストとも呼ばれる。開 発者はテストの実行前にテストに適した状態を整え、テスト実行後に元の

    状態を復元することが望ましい。 • テストフィクスチャは前提条件となるもの全てを指してる ◦ データベースにレコードを登録するアプリだったら、データベース(およ び事前に必要になるレコード)がテストフィクスチャ ◦ 文字をトークナイズするライブラリだったら、自然言語のテキストたちが テストフィクスチャ ▪ 小さなライブラリなら単なる文字列とか配列とかもそう テストフィクスチャとは
  8. • cf. xUnit - Wikipedia ◦ > テストを実行、成功させるために必要な状態や前提条件の集合を、フィク スチャ(英語版)と呼ぶ。これらはテストコンテキストとも呼ばれる。開 発者はテストの実行前にテストに適した状態を整え、テスト実行後に元の

    状態を復元することが望ましい。 • テストフィクスチャは前提条件となるもの全てを指してる ◦ データベースにレコードを登録するアプリだったら、データベース(およ び事前に必要になるレコード)がテストフィクスチャ ◦ 文字をトークナイズするライブラリだったら、自然言語のテキストたちが テストフィクスチャ ▪ 小さなライブラリなら単なる文字列とか配列とかもそう テストフィクスチャとは xUnitに準拠したユニットテストでは、 テストフィクスチャはなくてはならないもの(=前提)
  9. 「その中でも、データベースと密接に連携するWebアプリケーションに おいては、データベースにデータを投入し、そのデータをテストフィク スチャとして利用するテクニックは初期の頃から利用されてきました」

  10. (ほんと...?) 🤔

  11. • 「どんくらい昔からやってるのそれ?」 ◦ とりあえず Rails 2.x の段階ではすでにテストフィクスチャについて RailsGuides に言及されていた ので、まあフルスタックフレームワークが登

    場した初期の頃(2008年くらい)から存在してそう ◦ そして rails/rails v1.0.0 の activerecord にも普通にfixtures.rb があった ので、rails正式リリース当初(2005年くらい)から存在してそう ▪ @t_wada「エリック・ガンマとケント・ベックがチューリッヒからア トランタまでのフライトの中で"SUnitをJUnitに移植する"という伝説の ペアプロを行った、ってのがあって、それが今年(2022年)で25年」 • cf. 9. The 20th Anniversary of TDD by texta.fm Webアプリケーションのテストフィクスチャ
  12. • 「うん...そうなんだ...」 ◦ @t_wada「テスト駆動開発(TDD; Test-Driven Development)が生まれた のも2002年」とも言ってるので、割とフルスタックフレームワークでテス トが整備されたのってRuby on Railsは割と早かったんだと思う

    ◦ @t_wada「TDD原著が出たのも言葉が生まれた直後。2002年だった、と」 ◦ もしかしたら初期のStrutsとかで用意されてる可能性はあるけど調べきれ ず Webアプリケーションのテストフィクスチャ
  13. 「各種アプリケーションフレームワークではテストフィクスチャはどん なかんじで取り入れられてるの??」

  14. • Ruby on Railsの場合はどういう仕組みになってる? ◦ (ちょっと先に言っちゃったけど)YAMLファイルとか定義してDBに取り込 む ActiveRecord::FixtureSet ってクラスがある ◦

    cf. 4.2. テストフィクスチャのしくみ | Rails テスティングガイド - Railsガ イド Ruby on Railsのテストフィクスチャ
  15. None
  16. • CakePHPの場合はどういう仕組みになってる? ◦ class Cake\TestSuite\Fixture\TestFixture ってクラスがある ◦ PHPファイルに配列を書いて定義するかんじ ◦ cf.

    フィクスチャー | テスト - CakePHP 4.x Strawberry Cookbook CakePHPのテストフィクスチャ
  17. None
  18. • Laravelの場合はどういう仕組みになってる? ◦ おっと?? Laravel 9 ではModel Factories という概念を使ってるね?? ◦

    cf. Defining Model Factories | Database Testing - Laravel - The PHP Framework For Web Artisans ▪ Laravel 5.3くらいからあるっぽい?誰か補足して... • cf. Writing Factories | Database Testing - Laravel - The PHP Framework For Web Artisans Laravelのテストフィクスチャ
  19. None
  20. • xUnitの構成要素としてテストフィクスチャという概念は定義されてる • データベースを使ったテストフィクスチャは、Webアプリのフルスタックフレー ムワークが登場したタイミングには既にあった • 各種フルスタックフレームワークには、テストフィクスチャを扱う仕組みが大 体整備されている ◦ YAMLとか言語構文で提供されてたけど、Laravelでは早い段階からModel

    Factoriesという概念/実装が実現されていた ここまでのまとめ
  21. テストフィクスチャを分解してみる セットアップ戦略とライフサイクル

  22. (どこから分解すればよいの...?) 😳

  23. • セットアップについては Test fixture - Wikipedia に載っていた • > Test

    fixtures can be set up three different ways: in-line, delegate, and implicit. ◦ テストフィクスチャは”インライン”、”委譲”、”暗黙的”といった3通りの方 法でセットアップできるんやで 3つのセットアップ戦略
  24. • こんなかんじの構成を用いて説明するよ ◦ App ▪ class UsersClient ▪ class User

    ◦ TestSuite ▪ class TestCase (class PHPUnit\Framework\TestCase のイメージ) ◦ Test ▪ class UsersClientTest extends TestCase ※このあとお見せするソースコードの前提
  25. <?php declare(strict_types=1); namespace TestSuite; class TestCase { /** * @throws

    ExpectationFailedException */ public function assertSame($expected, $actual): void { if ($expected !== $actual) { throw new ExpectationFailedException('failure asserting that two variables'); } echo sprintf( 'success asserting that two variables %s and %s', var_export($expected, true) ?? 'null', var_export($actual, true) ?? 'null', ) . "\n"; } } class ExpectationFailedException extends \Exception {}
  26. <?php declare(strict_types=1); namespace App; class UsersClient { private $data =

    []; public function getUserById(string $id): ?User { if (!array_key_exists($id, $this->data)) { return null; } return $this->data[$id]; } public function setData(array $data): self { $isVector = array_values($data) === $data; $this->data = $isVector ? array_reduce(function ($carry, User $user) { $carry[$user->id] = $user; return $carry; }, $data) : $data; return $this; } } <?php declare(strict_types=1); namespace App; class User { private array $fields = []; public function __construct(array $fields) { $this->fields = $fields; } public function &__get(string $field) { return $this->fields[$field]; } public function __set(string $field, $value) { $this->fields[$field] = $value; } }
  27. • 日本語で扱うなら「インライン・セットアップ」でよいかな • > In-line setup creates the test fixture

    in the same method as the rest of the test. While in-line setup is the simplest test fixture to create, it leads to duplication when multiple tests require the same initial data. ◦ 要は、テストケース内で定義される形式 In-line setup
  28. • cf. Inline Setup at XUnitPatterns.com • テストメソッドから直接テストフィクスチャ にアクセスする、ってのが図示されてる •

    > Setting up the test fixture inline in each test is the most obvious way to build it. FYI: XUnitPatterns.comにはさっきのsetupも解説あり〼
  29. <?php declare(strict_types=1); namespace Test; use App\User; use App\UsersClient; use TestSuite\TestCase;

    class UsersClientTest extends TestCase { public function test_getUserById(): void { // given $fixture = [ new User(['id' => 1, 'name' => 'John']), new User(['id' => 2, 'name' => 'Paul']), ]; $client = (new UsersClient())->setData($fixture); // when $actual = $client->getUserById('1'); // then $expected = new User(['id' => 1, 'name' => 'John']); $this->assertEqual($expected, $actual); $this->assertEqual($expected->id, $actual->id); $this->assertEqual($expected->name, $actual->name); } } (new UsersClientTest())->test_getUserById(); https://3v4l.org/FhFpp テストケースの内部で定義してるから In-line setup
  30. • 日本語で扱うなら「委譲セットアップ」でよいかな • > Delegate setup places the test fixture

    in a separate standalone helper method that is accessed by multiple test methods. ◦ 複数のテストメソッドから呼ばれるstandaloneなヘルパーメソッドを経由 して呼び出される形式 Delegate setup
  31. • cf. Delegated Setup at XUnitPatterns.com • テストメソッドからは”Utility Method”を解 してテストフィクスチャにアクセスする、っ

    てのが図示されてる • > We are using a Fresh Fixture approach to build a Minimal Fixture for the use of this one test and we'd like to avoid Test Code Duplication. FYI: XUnitPatterns.comにはさっきのsetupも解説あり〼
  32. <?php declare(strict_types=1); namespace Test; use App\User; use App\UsersClient; use TestSuite\TestCase;

    class UsersClientTest extends TestCase { public function test_getUserById(): void { // given $fixture = $this->validUsersDataProvider(); $client = (new UsersClient())->setData($fixture); // when $actual = $client->getUserById('1'); // then $expected = new User(['id' => 1, 'name' => 'John']); $this->assertEqual($expected, $actual); $this->assertEqual($expected->id, $actual->id); $this->assertEqual($expected->name, $actual->name); } public function validUsersDataProvider(): array { return [ new User(['id' => 1, 'name' => 'John']), new User(['id' => 2, 'name' => 'Paul']), ]; } } (new UsersClientTest())->test_getUserById(); https://3v4l.org/bdABQ テストデータの生成を別のメソッドに委譲してるので Delegate setup
  33. • 日本語で扱うなら「暗黙的セットアップ」でよいかな • > Implicit setup places the test fixture

    in a setup method which is used to set up multiple test methods. This differs from delegate setup in that the overall setup of multiple tests is in a single setup method where the test fixture gets created rather than each test method having its own setup procedures and linking to an external test fixture. ◦ 単一のセットアップ処理で生成されて、それが複数のテストメソッドから 呼び出される ◦ 多くのアプリケーションフレームワークで標準装備されてるのは、この形 式のテストフィクスチャ管理になるはず Implicit setup
  34. • cf. Implicit Setup at XUnitPatterns.com • テストメソッドからではなく setUp() がテス

    トフィクスチャにアクセスする、ってのが図 示されてる • > SImplicit Setup is a way to reuse the fixture setup code for all the Test Methods in a Testcase Class. FYI: XUnitPatterns.comにはさっきのsetupも解説あり〼
  35. <?php declare(strict_types=1); namespace Test; use App\User; use App\UsersClient; use TestSuite\TestCase;

    class UsersClientTest extends TestCase { private $fixture; private $subject; public function setUp(): void { $this->fixture = [ new User(['id' => 1, 'name' => 'John']), new User(['id' => 2, 'name' => 'Paul']), ]; $this->subject = (new UsersClient())->setData($this->fixture); } public function test_getUserById(): void { // when $actual = $this->subject->getUserById('1'); // then $expected = current($this->fixture); $this->assertEqual($expected, $actual); } } $subject = new UsersClientTest(); $subject->setUp(); $subject->test_getUserById(); https://3v4l.org/OHsR1 テストケースの最初に暗黙的に定義しておくので Implicit setup
  36. <?php declare(strict_types=1); namespace TestSuite; class TestCase { protected function setUp():

    void { // do something... } /** * @throws ExpectationFailedException */ public function assertEqual($expected, $actual): void { if ($expected != $actual) { throw new ExpectationFailedException('failure asserting that two variables'); } echo sprintf( 'success asserting that two variables %s and %s', var_export($expected, true) ?? 'null', var_export($actual, true) ?? 'null', ) . "\n"; } } https://3v4l.org/OHsR1 こんなかんじでテストケースのベースになる classで setUp() をcallするように用意しておく
  37. • cf. Fresh Fixture at XUnitPatterns.com • > We design

    and build the test fixture such that only a single running of a single test will use it. We construct the fixture as part of running the test and tear down the fixture when the test has finished. ◦ テストケース実行するたびにビルドして、テストケース終わったら破棄 ◦ 基本的なフルスタックフレームワークはこれが前提だと思うで • > When each test creates a Fresh Fixture, it prevents Erratic Tests and is more likely to result in Tests as Documentation. ◦ Fresh Fixtureを採用すると、不規則なテストがなくなるし”Tests as Documentation(ドキュメントとしてのテスト)”となってよいわよ ◦ あれ?最強そうじゃん?? これらのsetupは”Fresh Fixture”という戦略の上にある
  38. 弟子「じゃあFresh Fixtureでよいじゃん」 師匠「待て待て、テストスイートで大事なのはそれだけじゃないのよ」

  39. • cf. abseil / Software Engineering at Google • Part.11

    Testing Overview の序文 ◦ > If you have a robust testing practice, you needn’t fear change—you can embrace it as an essential quality of developing software. The more and faster you want to change your systems, the more you need a fast way to test them. ▪ システムをスピード感持って変更するためには、より高速なテスト実 行環境が必要だよ • Fresh Fixtureは都度読み込むから実行コスト/速度がかかりそうね...?? そもそも、テストスイートに重要な観点って何?
  40. • cf. Software Design 2022年3月号|技術評論社 ◦ @t_wadaさんの「自動テストとテスト駆動開発、その全体像」の序盤 ◦ > 自動テストが必ず満たすべき性質が2つあります。自己検証可能

    (Self-Validating)であることと、繰り返し可能(Repeatable)であるこ と ▪ 繰り返し可能であるためにFresh Fixtureは必須になりそう ◦ > 自動テストに強く推奨される性質が2つあります。独立している (Independent / Isolated)ことと高速である(Fast)こと ▪ 独立していることを担保するためにテストフィクスチャも分離したい ▪ 高速であるためには、Fresh Fixtureだとオーバーヘッドが大きいかも そもそも、テストスイートに重要な観点って何?
  41. • cf. 現在時刻が関わるユニットテストから、テスト容易性設計を学ぶ - t-wadaの ブログ ◦ > Repeatable であるとは、テストを実行するだけで、いつでも、何回でも

    同じように動くということです。環境や時間によってテスト結果が変化し てしまう場合、そのテストは Repeatable ではありません。 テストコードが 変わっていないのに、状況によって通ったり通らなかったりするテスト は、書籍『xUnit Test Patterns』ではErratic Test (不安定なテスト)と 表現されています。 • 何度も繰り返し実行するので速度も大事だし、安定して動くことも大事 FYI: 「よいユニットテストとはRepeatable」
  42. • cf. Shared Fixture at XUnitPatterns.com ◦ > To execute

    an automated test, we require a text fixture that is well understood and completely deterministic. Setting up a Fresh Fixture can be time consuming, especially when dealing with complex system state stored in a test database. ▪ “Fresh Fixture”だと都度テストフィクスチャをsetupするから時間かか るのよね ▪ あっ基本的にデータ投入して用意しとくから使ってや!なあ兄ちゃん • これってテストケースの実行サイクルとは別に、”すでに用意され ているテストフィクスチャ”として扱う必要があるのよね 高速化のためのテストフィクスチャ戦略
  43. • Shared Fixtureを実現するには、”完全に決定論的であり、よく理解されたテス トフィクスチャ"が必要 ◦ cf. 決定論 - Wikipedia ▪

    > 決定論(英: determinism)とは、あらゆる出来事は、その出来事に 先行する出来事のみによって決定している、とする哲学的な立場。 ◦ 要は、状態とかが諸々固定されたテストフィクスチャが必要になる ▪ 「あれ?じゃあ同じテストケースでも、前提条件が異なるテストフィ クスチャが必要な時はどうするの...?」「...君のような勘のいいガキは 嫌いだよ」 決定論的なテストフィクスチャが必要なのだけど...
  44. • cf. Standard Fixture at XUnitPatterns.com ◦ > A Standard

    Fixture is a way to reuse the same fixture design in several tests without necessarily sharing the same fixture instance. ▪ 使い回せるような、Standardなテストフィクスチャを設計しような! • cf. Minimal Fixture at XUnitPatterns.com ◦ > A key part of understanding a test is understanding the test fixture and how it influences the expected outcome of the test. Tests are much easier to understand if the fixture is small and simple. ▪ 余計な要素を削ることでテストフィクスチャの内容を把握するぞ! • これらの戦略も決定論的にテストフィクスチャを整理するための戦略 高速化のためのテストフィクスチャ戦略
  45. • ここまでを整理すると ◦ 実行速度を求めるならば事前にセットアップしておけるShared Fixtureが 効果的なのはわかる ▪ でも決定論的ではなく多様な前提条件を表現する必要がある ▪ テストフィクスチャならばFresh

    Fixtureで用意するしかないのでは? • 要はトレードオフなのだと思う ◦ 決定論的なテストフィクスチャを”表現”するのそんなむずかしくない ▪ 内容を調整するのは大変だが、フィクスチャが洗練され高速化も期待 ◦ 多様なパターンがあるテストフィクスチャ群を表現するのが難しいので、 それが柔軟に表現できる"表現力があると便利!ってことじゃない?? なんかたくさんの用語が出てきたし、整理するよ
  46. CakePHP Fixture Factoriesの登場 ようやく本題

  47. None
  48. • vierge-noire/cakephp-fixture-factories ◦ CakePHPで複雑なテストフィクスチャを柔軟に扱うときに便利ってわけ ◦ これのおかげで、Laravelでは既に実現されてるModel FactoriesをCakePHP で使えるようになった🙌 ここでCakePHP Fixture

    Factoriesの登場だ!!
  49. • 基本的に class UserFactory extends BaseFactory というかんじで読み込む 具体的にはこんな感じに使えるよん <?php declare(strict_types=1);

    namespace Test\Factory; use CakephpFixtureFactories\Factory\BaseFactory; use Faker\Generator; class UserFactory extends BaseFactory { protected function getRootTableRegistryName(): string { return 'Users'; } protected function setDefaultTemplate(): void { $this->setDefaultData(function (Generator $faker) { return [ 'screen_name' => $faker->userName(), 'name' => $faker->name(), 'icon_url' => $faker->imageUrl(128, 128), ]; }); } }
  50. • UserFactory::make() でModel Factoryがセットアップされる ◦ この時点では class UserFactory に定義されてる protected

    function setDefaultTemplate(): void の中で定義されているデータをセットアップす るだけ 具体的にはこんな感じに使えるよん public function test_getUserById(): void { // given $userFactory = UserFactory::make();
  51. • $user = UserFactory::make()->getEntity() でエンティティが出てくる ◦ UserFactory::make()->getEntities() で配列に入ったエンティティ群が出て くる 具体的にはこんな感じに使えるよん

    public function test_getUserById(): void { // given $users = UserFactory::make(2)->getEntities(); $client = (new UsersClient())->setData($users);
  52. • UserFactory::make($data) でプロパティに値を渡せます ◦ 無名関数も渡せます 具体的にはこんな感じに使えるよん public function test_getUserById(): void

    { // given $takoba = UserFactory::make(['name' => 'takoba'])->persist(); $somebody = UserFactory::make(function (UserFactory $factory, Faker\Generator $faker) { return ['screen_name' => $faker->userName()]; }, 3)->persist();
  53. public function test_getUserById(): void { // given $users = UserFactory::make(2)->persist();

    // 以下は同義 $fixture = [new User(['name' => 'John']), new User(['name' => 'Paul'])]; $UsersTable = TableRegistry::getTableLocator()->get('Users'); $users = $UsersTable->saveManyOrFail($fixture); • CakePHPだと UserFactory::make()->persist() とかってやると cakephp/orm の class Cake\ORM\Table で定義された class UsersTable extends Table でデータ が保存されて、 class Cake\ORM\Entity で定義されてる class User extends Entity のインスタンスがヒュッと手に入る! 具体的にはこんな感じに使えるよん
  54. public function test_getUserById(): void { // given $users = UserFactory::make(2)

    ->with('UserAddresses', UserAddressFactory::make(3)) ->persist(); • そして UserFactory::make()->with('UserAddresses', UserAddressFactory::make(3)) みたいなかんじで簡単にassociationsも表現で きる!!! 具体的にはこんな感じに使えるよん
  55. public function test_getUserById(): void { // given $users = UserFactory::make(2)

    ->listeningToModelEvents(‘Model.beforeMarshal’) ->getEntity(); • UserFactory::make()->listeningToModelEvents('Model.beforeMarshal') とする ことでModelのイベントを発火することができる ◦ 基本、CakePHP Fixture Factories側で一通りoffにしているので明示的に呼 び出す必要がある 具体的にはこんな感じに使えるよん
  56. public function test_getUserById(): void { // given $users = UserFactory::make(2)

    ->listeningToBehaviors(‘Sluggable’) ->getEntity(); • UserFactory::make()->listeningToBehaviors('Sluggable') とすることでModelの Behaviorを呼び出すことができる ◦ CakePHP Fixture Factories側ではTimestampBehaviorだけ有効にしてある ◦ 'TestFixtureGlobalBehaviors' というConfigに有効にしたいBehaviorを設定 できる 具体的にはこんな感じに使えるよん
  57. (ようやくLaravelに追いついたね!やったね) 🙌

  58. • CakePHP3以上をサポートしてる ◦ 基本的にCakePHP3で導入された cakephp/orm のTableクラスとEntityクラ スを前提に実装されてる ◦ CakePHP2はがんばったら使えるかもだけど恩恵はあまり受けられないか な...

    ▪ フレームワークの前提が namespaced じゃないので、どう読み込むか は課題だけどたぶん使えそうではあるけど公式にはサポートされてな いし、そもそもEntityの恩恵は得られない サポート範囲
  59. None
  60. • vierge-noire/test-database-cleaner を使ってデータベースをcleanupするライ ブラリも提供してたりはする ◦ これはLaravelでも他のアプリでも使えて、なおかつ速いぜ!という売り ◦ ちなみにCakePHPアプリだと cakephp/migrationを用いる方法 でスキーマ

    を作成して FixtureStrategyによるrefleshをする方法 が推奨されてる • データを生成したりデータベースへ登録するにはcakephp/ormに準拠したTable クラスとEntityクラスが必要になる ◦ cf. cakephp-fixture-factories/no_cake_associations.md at main · vierge-noire/cakephp-fixture-factories ◦ 将来的には拡張ができるようになるかもしれないけど、現状は一通り EventCompilerとかを書かないと差し替えられないね... ほんとにCakePHPに関わらず使えるの...?
  61. None
  62. None
  63. • 特にissueとかないけど、なんとなくnon-CakePHP Applicationにも開放してい きたい気持ちはありそう...? ◦ とはいえ、特に言及は見つけられなかった • cakephp/orm 便利だから使ってみてよ!(そうじゃないけど便利なのは便利) ◦

    cf. データベースアクセス & ORM - CakePHP 4.x Strawberry Cookbook 使えないんじゃん💢
  64. • CakePHP 4.3.0から、 class Cake\TestSuite\Fixture\TestFixture をベースにし たテストフィクスチャのライフサイクル戦略を設定することができるように なった • 以下のようなクラスがプリセットで用意されている

    • class TruncateStrategy implements FixtureStrategyInterface ◦ テストケース実行後にテーブルをTRUNCATEする • class TransactionStrategy implements FixtureStrategyInterface ◦ BEGIN; でトランザクション張って、 tearDown() 時に ROLLBACK; • 先ほど紹介したテストフィクスチャの戦略を実現できる...? FYI: CakePHPのフィクスチャステートマネージャ
  65. None
  66. まとめ あとスライド5枚!

  67. • テストフィクスチャ、結局は決定論的なものも複雑で動的なものもバランスよ く使っていく必要があるよ! • その”複雑で動的なもの(≒Fresh Fixture)”を簡単に表現できるのが vierge-noire/cakephp-fixture-factories ってわけ! • CakePHPアプリケーションには容易に導入できるけど、まだまだ

    cakephp/orm に依存してるので今後に期待だ! ???「3行で頼んだ」
  68. 紹介した概念をバランスよく使って、 メンテナブルでファストなテストスイートを運用していこうな!!!

  69. 終 制作・著作

  70. • Test fixture - Wikipedia • Rubyのテスティングフレームワークの歴史(2014年版) - 2014-11-06 -

    クク ログ • test-unit - Ruby用単体テストフレームワーク • index at XUnitPatterns.com ◦ Inline Setup at XUnitPatterns.com ◦ Delegated Setup at XUnitPatterns.com ◦ Implicit Setup at XUnitPatterns.com ◦ Shared Fixture at XUnitPatterns.com ◦ Fresh Fixture at XUnitPatterns.com 参考文献
  71. • Amazon.co.jp: xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature

    Series (Fowler)) (English Edition) 電子書籍: Meszaros, Gerard: 洋書 • abseil / Software Engineering at Google • Software Design 2022年3月号|技術評論社 • vierge-noire/cakephp-fixture-factories: CakePHP Fixture Factories • CakeFest 2020 - Fixture Factories Plugin - Juan Pablo Ramirez - YouTube • データベースアクセス & ORM - CakePHP 4.x Strawberry Cookbook • 現在時刻が関わるユニットテストから、テスト容易性設計を学ぶ - t-wadaのブ ログ • Google Slidesにソースコードを貼り付ける - zenn.dev ◦ 参考文献というよりSpecial Thanks 参考文献