初投稿です。
試験時にテストケースをいちいちデータベースに値を入れたり、通信を投げたりするのは非常に面倒です。今回はFactoryMethodを用いて本番用データとテスト用データを使い分ける方法を記します。サンプルコードはC#です。
FactoryMethodとは
- クラスのインスタンスの生成の責務を「〇〇Factory」に負わせる手法です。
- インスタンスを使う側のクラスは、コンストラクタを直接呼び出すのではなく、Factoryクラスのメソッドを呼び出し、その戻り値を利用します。
これにより、インスタンスを生成するときに行う処理をFactoryクラスの中で一元管理することができ、保守性が向上します。
ここでは、Factoryクラスでインスタンスを生成するときに、本番用とテスト用で扱うデータ様式を分岐する方法を紹介します。
サンプルの作成
ToDo(やりたいこと)
- 例として、本番用のデータをSQLite、テスト用のデータをCSVで管理する
- 素材の在庫状況を扱う「Materials」というデータ単位を対象とする
- データクラスとして、MaterialEntityを作成する。
- IMaterialRepositoryというインターフェースを作成し、Get()とSet()を定義する
- インターフェースの実装クラスとして、MaterialSQLとMaterialFakeを作成する
- 上記二つのクラスのインスタンスを生成する静的クラスとしてMaterialFactoryを作成し、CreateMaterial()を定義する
- CreateMaterial()では、本番用かテスト用かによって、作成するインスタンスを分岐させるようにする
データの作成
カラムを左から、素材ID、素材名、在庫数とします。
本番用はSQLiteでMaterialsテーブルを作成します。
MaterialEntityの作成
データクラスのMaterialEntityを作成します。特に理由がない限りは完全コンストラクタ型にしておきます。
namespace FactoryMethod
{
// 素材のデータクラス
public sealed class MaterialEntity
{
// コンストラクタ
public MaterialEntity(int id, string name, int amount)
{
Id = id;
Name = name;
Amount = amount;
}
// 素材ID
public int Id { get; }
// 素材名
public string Name { get;}
// 素材量
public int Amount { get; }
}
}
リポジトリのインターフェースを作成
IMaterialRepositoryを作成します。リポジトリは、「あるデータのやりとりにおける単位」という認識でOKです。例えるなら「入荷や配達を行う宅配便の配達員さん」みたいなもんです。
今回はこのインターフェースにSet()とGet()を定義します。適宜Update()やDelete()などを定義してもいいでしょう。
namespace FactoryMethod
{
// 素材のリポジトリ
public interface IMaterialRepository
{
// 素材の取得
public MaterialEntity Get();
// 素材の設定
public void Set(MaterialEntity entity);
}
}
実装クラスの作成
データを扱うクラスとして、本番用はMaterialSQLを、テスト用はMaterialFakeを作成します。それぞれインタフェースを実装させます。本番用はSQLを、テスト用はSystem.IOのファイル処理を用います。
namespace FactoryMethod
{
internal class MaterialSQL : IMaterialRepository
{
public MaterialEntity Get()
{
//・・・(SQLの処理)
}
public void Set(MaterialEntity entity)
{
//・・・(SQLの処理)
}
}
}
namespace FactoryMethod
{
internal class MaterialFake : IMaterialRepository
{
public MaterialEntity Get()
{
//・・・(CSVファイルの処理)
}
public void Set(MaterialEntity entity)
{
//・・・(CSVファイルの処理)
}
}
}
Factoryクラスの作成と生成するインスタンスの分岐
上記2つのクラスのインスタンスを生成する責務を担うMaterialFactoryを作成します。本番用の場合はMaterialSQLの、テスト用の場合はMaterialFakeのインスタンスを生成することとします。configファイルに設定値を待っても良いですし、DebugとReleaseの場合で分けても良いでしょう(今回は後者です)。
namespace FactoryMethod
{
public static class MaterialFactory
{
public static IMaterialRepository CreateMaterial()
{
# if DEBUG
return new MaterialFake();
#else
return new MaterialSQL();
#endif
}
}
}
呼び出し
呼び出す側は、実装クラスを直接呼び出すのではなく、MaterialFactory.CreateMaterial()を呼び出し、IMaterialRepository型の変数に代入します。その変数からメソッドを呼び出して利用します。
namespace FactoryMethod
{
public class MainClass
{
static void Main()
{
//本番時はMaterialSQLが、テスト時はMaterialFakeのインスタンスが生成される
IMaterialRepository material = MaterialFactory.CreateMaterial();
//Setメソッド
material.Set(new MaterialEntity(4, "鉛", 500));
//Getメソッド
var entity = material.Get();
}
}
}
サンプルプログラムの作成は以上です。
メリット
- 単体テストなどの机上デバッグを行う際に本番用のデータを扱う必要がないため、テストが効率化される。
- データの様式が変わっても(データベースからjsonとか)、Repositoryインターフェースを実装させているデータのやり取りを行う箇所のみ変更すれば良いため、変更容易性が高い。
以上です。