久々の投稿です。よろしくお願いします。
課題
- 元々Excelファイルをアップロードする処理はあったが、テストが無かった
- テストデータは手作成でExcelを作ってアップロードし、目視で確認していたため効率が悪かった。
TL;DR
- テストデータ作成用Factoryクラスを作る(LaravelのデータベースFactoryとは別)
- テストデータはFactoryクラスで作成
- テストデータのアップロードはUploadedFileクラスを使う
xlsxファイルFactoryクラスの作成
今回は「XlsxFileFactory」とでもしましょうか。
class XlsxFileFactory
{
public array $importFileHeaders;
public SpreadSheet $workBook;
public function __construct(array $headers)
{
$this->workBook = new SpreadSheet();
$this->importFileHeaders = $headers;
}
public function createDummyFile(int $createCount): void
{
$this->write($createCount);
$writer = new Xlsx($this->workBook);
$writer->save(base_path('storage/app/public/') . 'test.xlsx');
}
private function write(int $createCount): void
{
$workSheet = $this->workBook->getActiveSheet();
$faker = Factory::create('ja_JP');
// ヘッダの書き込み(ヘッダは配列をそのまま出力)
$workSheet->fromArray($this->importFileHeaders, 'エラー', 'A1');
// データ部の書き込み(データ部は書式を設定したりするのでループで処理)
for ($i = 2; $i < $createCount+2; $i++) {
// 数値5文字を標準書式で作成
$workSheet->setCellValue('A'.$i, $faker->numerify('######'));
// 日付を書式設定して入力
$workSheet->getStyle('B'.$i)->getNumberFormat()->setFormatCode('yyyy/m/d h:mm');
$workSheet->setCellValue('B'.$i, Date::PHPToExcel($faker->dateTime())); // DateTime型を渡すと書式設定通りに値が入る
// 数値を文字として入力
$workSheet->setCellValueExplicit('C'.$i, $faker->numberBetWeen(1,10), DataType::TYPE_STRING);
}
}
}
テストコードの作成
class ExampleTest extends TestCase
{
public function setUp(): void
{
// ケースごとにセットアップしたいものをここに書く
// hogehoge...
parent::__construct();
}
/**
* @test
*/
public function Excelのアップロードテスト()
{
$count = 10;
$headers = ['標準書式', '日付', '数値を文字で'];
$fileFactory = new XlsxFileFactory($headers);
// この処理を行うと「storage/app/public/test.xlsx」が出来上がる
$fileFactory->createDummyFile($count);
// アップロードのテスト
$file = new UploadedFile(
base_path('storage/app/public/') . 'test.xlsx',
'test.xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // MS ExcelのMimeType
0,
true // テストフラグをtrueにする
);
// POSTのボディ部(フォームデータ)にfileを渡す
$this->post(route('import'), [
'import_file' => $file,
]);
}
public function tearDown(): void
{
if (Storage::disk('public')->exists('test.xlsx')) {
Storage::disk('public')->delete('test.xlsx');
}
}
}
tearDownでファイルを消しているので、テストした跡も残らず
GitHubActionsやCircleCI等のテストランナーで活用できるかと思います。
テスト中は、ファイルの中身を見たいシーンもあるでしょうから、コメントアウトしとくと良いですね。
本当はStorageファサードで全部解決できれば良かったのですが
Storage::get()
やnew File()
等で取得しようとすると中身のコンテンツがStringで拾われることとなり
テキストデータをPOSTする羽目になってしまったので、今回こういう形になりました。
もっと他に良い方法をご存じの方がいらっしゃれば、ご意見頂きたいと思います。