リリース前に、アプリは入場チケットの QR コードを PNG として保存済みで、画面上のプレビューも正常に見える。それでも実際の読み取りでは、拡大縮小時の補間、端の切り落とし、コードと背景のコントラスト不足によって失敗することがある。「ファイルが存在する」だけを確認するより、クラウド Mac 上でアプリが最終的に出力した画像を取り出し、Vision でもう一度読み取って内容を照合するほうが確実だ。これは iOS の回帰テストに組み込みやすい、ファイル単位の検証となる。
まず合格条件を決める
ここで検証するのはアプリが実際に書き出した PNGであり、テストスクリプトが自分で生成した QR コードではない。固定のテストデータは case-123 とし、アプリのテスト用エントリーポイントから、対応する画像をサンドボックス内の Documents/qr-sample.png に書き出す。製品が QR コードを画面に描画するだけなら、既存のエクスポート機能でファイルを取得する。テスト専用のジェネレーターを別に作り、その出力を製品の出力として扱ってはいけない。
合格条件は三つ。アプリのコンテナからファイルを取り出せること、Vision が QR コードをちょうど一つ検出すること、復号した文字列が期待値と完全に一致することだ。ファイルのハッシュは主な判定基準に向かない。エンコード設定や PNG のメタデータが変われば、QR コードの内容が同じでもバイト列は変わり得る。
ここでいう「読み取れる」は、エクスポートしたデジタル画像に限った話だ。カメラのピント、画面の明るさ、印刷サイズには別の変動要因があるため、ファイル単位の検証は実際のスキャンに代わるものではない。
シミュレータから最終ファイルを取り出す
まず、起動中の iOS シミュレータでテスト用エントリーポイントを実行し、アプリが指定したパスに確かに書き込んだことを確認する。次のコマンドでは、環境変数 BUNDLE_ID にテスト対象アプリの実際の bundle identifier が設定されている必要がある。get_app_container は、そのアプリのデータコンテナのパスを返す。
test -n "$BUNDLE_ID" || exit 2
APP_DATA=$(xcrun simctl get_app_container booted "$BUNDLE_ID" data) || exit 1
mkdir -p artifacts
cp "$APP_DATA/Documents/qr-sample.png" artifacts/qr-sample.png
file artifacts/qr-sample.png
cp が失敗したら、まずアプリが現在起動中のシミュレータで動いているか、エクスポート処理が本当に Documents に書き込んだかを調べる。開発用 Mac にある同名の画像で埋め合わせてはいけない。それでは回帰テストがアプリの出力を検証できなくなる。アプリが毎回複数のファイルを出力する場合は、テスト用エントリーポイントで一定のファイル名を使い、実行のたびに古いファイルを削除して、前回の結果を誤って読まないようにする。
Vision で復号し、データを照合する
以下を作業ディレクトリに qr-check.swift として保存する。スクリプトは PNG のパスと期待するテキストを受け取り、入力、認識、照合のいずれかに失敗するとゼロ以外の終了ステータスを返すため、パイプラインでそのまま判定できる。
import Foundation
import Vision
guard CommandLine.arguments.count == 3 else {
fputs("usage: swift qr-check.swift IMAGE EXPECTED\n", stderr)
exit(2)
}
let url = URL(fileURLWithPath: CommandLine.arguments[1])
let expected = CommandLine.arguments[2]
let request = VNDetectBarcodesRequest()
request.symbologies = [.qr]
do {
try VNImageRequestHandler(url: url, options: [:]).perform([request])
let codes = (request.results ?? []).filter { $0.symbology == .qr }
guard codes.count == 1,
codes[0].payloadStringValue == expected else {
fputs("QR count or payload mismatch\n", stderr)
exit(1)
}
print("QR payload verified")
} catch {
fputs("QR image could not be analyzed\n", stderr)
exit(1)
}
swift qr-check.swift artifacts/qr-sample.png case-123 を実行する。認識したコードの数だけを確認してはいけない。読み取りに成功しても、古い注文番号、誤った接頭辞、途中で切れたテキストが入っている可能性がある。また、テストデータに本物の認証情報は含めない。業務データに機密フィールドがある場合は、形式と解析処理を確認できる専用の無効なテスト値を使えばよい。
失敗箇所を段階的に切り分ける
まず出力ファイルを調べ、次に認識結果を確認し、最後に QR コードの生成処理へ戻る。この順番なら、「そもそも出力されていない」と「出力されたが読み取れない」を分けられる。
| 症状 | 最初に確認する点 |
|---|---|
| PNG が見つからない | シミュレータの選択、bundle identifier、サンドボックス内のパス、古いファイルの削除 |
| Vision がコードを見つけられない | 画像が切り取られていないか、小さく縮小されていないか、非可逆の処理を受けていないか |
| 複数のコードが認識される | 出力画像に別の QR コードが混入していないか |
| データが一致しない | エンコード前のテキスト、文字エンコーディング、キャッシュ、出力のタイミング |
アプリが Core Image で QR コードを生成している場合は、まずジェネレーターの outputImage から画像の境界全体を保持し、整数倍に拡大してから PNG に書き出す。共有やスクリーンショットの経路で、補間を伴う縮小がもう一度行われていないかも確認する。プレビューが滑らかに見えても、マス目の輪郭が鮮明とは限らない。ダークテーマでは最終ファイルの色も確認したい。画面の背景と出力画像の背景は同じとは限らず、透明部分は別の背景色の上でコントラストを失うことがある。
誤判定を招かないために
テスト画像、明確なデータ、出力先のパスをそれぞれ一つに固定しつつ、画像は毎回、その回に実行したアプリの出力から取得する。長さの異なるデータ、非 ASCII 文字、区切り文字を含むデータを検証するなら、個別のテストケースを作って一件ずつ照合する。「複数の期待値のどれかを含めばよい」という条件にはしない。QR コードの復号に成功しても、業務用リンクが使えるとは限らない。遷移の確認が必要なら、別のテスト層で解析とルーティングを検証する。
回帰テストのフローに組み込む
既存のシミュレータテストに続けて、画像の取得コマンドと Swift スクリプトを実行し、どちらかがゼロ以外の終了コードを返したら、その回のジョブを止める。失敗時には、その回の PNG とスクリプトのエラー出力を残す。エクスポート先が変わったのか、画像処理によって品質が劣化したのかを判断しやすくなる。成功時は合格状態を記録するだけでよく、業務データを含む画像を長期間保存する必要はない。
最後に確認するのは四点。画像がアプリの最終出力に由来すること、前回の残存ファイルを読んでいないこと、Vision がちょうど一つのコードを読み取ること、データが固定のテスト値と完全に一致することだ。これで、ファイル単位の QR コード回帰テストに明確で再現可能な境界を設けられる。画面表示や印刷物として提供する場合は、実機でのスキャン検証も追加する。
よくある質問
Vision の検査だけで実機のカメラスキャンは不要になりますか?
いいえ。画像の復号と内容は確認できますが、画面の明るさ、カメラのピント、印刷サイズは実環境で別途検証します。
検査スクリプト内で QR コードを生成してはいけませんか?
それではスクリプト自身しか検証できません。アプリが保存した最終 PNG を使い、縮小、色変更、切り抜きなどの不具合を検出します。
独占利用できる物理Mac miniで次のステップを検証
VMCommitではM4搭載の物理Mac miniを独占利用できます。日・週・月・四半期単位でレンタル可能です。モデルとロケーションを選ぶと、コンソールでリアルタイムの空き状況を確認できます。