Asakusa FrameworkのFlowPartTester・JobFlowTesterのメモ。
|
|
AsakusaFWでジョブフローのテストを実行するクラスがJobFlowTester、フロー部品(FlowPart)のテストを実行するクラスがFlowPartTester。
フローをテストする場合、JobFlowTester/FlowPartTesterに入力データ・検証データ・検証ルールを指定する。
JobFlowTester・FlowPartTesterの一番の違いはテスト対象クラスの渡し方で、それ以外は同じ。
ただしJobFlowTesterを使うと、実行時に外部環境が必要になる。[2017-06-03]
(Importer/Exporterに従ってデータにアクセスする)
例えばWindGate JDBCを使っていると、実際にDBアクセスする(テスト環境用のDBが必要になる)。
例えば@directio.csv.file_nameが付けられた項目にはテスト環境のファイルのパスが入ってくる。
こういった場合、ジョブフローであってもFlowPartTesterでテストすると、そういった外部環境は不要になる。
(Flow DSLとしてのJobFlowとFlowPartの違いは@Import/@Exportが書かれているかどうか、すなわちImporter/Exporterが記述されているかどうか。FlowPartでは@Import/@Exportは不要)
(例えばWindGate JDBCを使っていてもDBアクセスは行わない(WindGate
JDBCかどうかはImporter/Exporterに書かれているので、それらを使わないFlowPartTesterには関係がない))
(例えば@directio.csv.file_nameであっても自動的にパスは入らず、テストデータに書いた値が入ってくる)
テストにはJUnitを使う。
テストを実行すると、テストドライバーが入力データを読み込んでファイルとして用意し、テスト対象クラスのフローを実行する。
そして実行結果のファイルを読み込み、検証ルールに従って検証データと突き合わせてチェックする。
フローのテストを実行するには、Asakusa Frameworkの実行環境が必要となる。[2017-12-08]
大昔は実行環境がHadoopしか無かったので、Hadoopを実行できないWindowsではフローのテストは実行できなかった。
AsakusaFW 0.7.1以降はスモールジョブ実行エンジンを使う事でWindowsでもテストを実行できるようになった。
AsakusaFW 0.10.0で、フローのテストはVanilla(pure
Javaの実行エンジン)を使うようになった。
(スモールジョブ実行エンジンは使われなくなった)
AsakusaFWのサンプルプロジェクトのCategorySummaryJobのテストクラスの例。
import com.asakusafw.testdriver.JobFlowTester; import org.junit.Test;
/** * {@link CategorySummaryJob}のテスト。 */ public class CategorySummaryJobTest {
@Test public void describe() { JobFlowTester tester = new JobFlowTester(getClass()); // 入力データを指定 tester.input("salesDetail", SalesDetail.class).prepare("CategorySummaryJobTest.xls#salesDetail"); tester.input("storeInfo", StoreInfo.class).prepare("CategorySummaryJobTest.xls#storeInfo"); tester.input("itemInfo", ItemInfo.class).prepare("CategorySummaryJobTest.xls#itemInfo"); // 検証データおよび検証ルールを指定 tester.output("categorySummary", CategorySummary.class).verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule"); tester.output("errorRecord", ErrorRecord.class).verify("CategorySummaryJobTest.xls#errorRecord", "CategorySummaryJobTest.xls#errorRecord_rule"); // テストの実行 tester.runTest(CategorySummaryJob.class); } }
入力データや検証データ等の指定方法は後述。
テストの実行(runTestメソッド)には、テスト対象のジョブフロークラスを渡す。
AsakusaFWのサンプルプロジェクト(のCategorySummaryJobをフロー部品化した)CategorySummaryFlowPartのテストクラスの例。
import com.asakusafw.testdriver.FlowPartTester; import com.asakusafw.vocabulary.flow.FlowDescription; import com.asakusafw.vocabulary.flow.In; import com.asakusafw.vocabulary.flow.Out; import org.junit.Test;
/** * {@link CategorySummaryFlowPart}のテスト。 */ public class CategorySummaryFlowPartTest {
@Test public void describe() { FlowPartTester tester = new FlowPartTester(getClass()); // 入力データを指定 In<SalesDetail> salesDetail = tester.input("salesDetail", SalesDetail.class).prepare("CategorySummaryFlowPartTest.xls#salesDetail"); In<StoreInfo> storeInfo = tester.input("storeInfo", StoreInfo.class).prepare("CategorySummaryFlowPartTest.xls#storeInfo"); In<ItemInfo> itemInfo = tester.input("itemInfo", ItemInfo.class).prepare("CategorySummaryFlowPartTest.xls#itemInfo"); // 検証データおよび検証ルールを指定 Out<CategorySummary> categorySummary = tester.output("categorySummary", CategorySummary.class).verify("CategorySummaryFlowPartTest.xls#categorySummary", "CategorySummaryFlowPartTest.xls#categorySummary_rule"); Out<ErrorRecord> errorRecord = tester.output("errorRecord", ErrorRecord.class).verify("CategorySummaryFlowPartTest.xls#errorRecord", "CategorySummaryFlowPartTest.xls#errorRecord_rule"); // テストの実行 FlowDescription flowPart = new CategorySummaryFlowPart(salesDetail, storeInfo, itemInfo, categorySummary, errorRecord); tester.runTest(flowPart); } }
入力データや検証データ等の指定方法は後述。
テストの実行(runTestメソッド)には、テスト対象フロー部品のインスタンスを渡す。
FlowPartのインスタンスを作成するには入力や出力データを表すInやOutが必要になるので、tester.input()やtester.output()で生成する。
ジョブフロークラスであっても、FlowPartTesterを使ってテストすることが出来る。[2017-06-03]
ジョブフローとフロー部品の違いは、コンストラクターの各引数に@Import/@Exportが指定されているかどうかだけなので(FlowPartTesterを使う場合はそれらが無視されるだけなので)、形式的には全く同じ。
AsakusaFW 0.10.0で、FlowPartTesterのrunTestメソッドにラムダ式を渡せるようになった。[2017-12-10]
FlowPartクラスを作らなくてもフローのテストが出来るので、フローを使ってオペレーターのテストをする場合に便利。
BatchContextのバッチ引数(実行時引数)を使っているフローをテストする場合、JobFlowTester/FlowPartTesterに バッチ引数を指定する。
tester.setBatchArg("argName", "value");
ちなみに、バッチ引数の内容をstatic変数にキャッシュするような使い方をしている場合は、テストの実行に注意が必要。
デフォルトの状態では、テストの実行にはHadoopを起動するので、テストケース毎に毎回static変数はクリアされる。
一方、スモールジョブ実行エンジンを使っている場合はテストケース間で同一のJavaVMが使われるので、static変数は最初に実行された状態でキャッシュされたままになる。
テスト用にキャッシュをクリアするような仕組みを自分で組み入れる必要がある。
テストの入力データはJobFlowTester/FlowPartTesterのinputメソッド・prepareメソッドで指定する。
ジョブフローの場合 tester.input("salesDetail", SalesDetail.class).prepare("CategorySummaryJobTest.xls#salesDetail");
フロー部品の場合 In<SalesDetail> salesDetail = tester.input("salesDetail", SalesDetail.class).prepare("CategorySummaryFlowPartTest.xls#salesDetail");
フロー部品の場合はテスト対象FlowPartのコンストラクターに渡す必要があるので、変数に入れておく。
inputメソッドでは、テスト対象クラスの入力ポート名とデータモデルクラスを指定する。
prepareメソッドでは、入力データを指定する。
prepareメソッドでは、入力データを指定する。
入力データはExcelやJSONで指定できる。
Excelの場合はファイル名とシート名を「#」で区切って指定する。
ファイルは、src/test/resourcesの下のテストクラスと同一パッケージの場所に配置しておく。
AsakusaFW 0.9.1以降では、(Excelでデータを記述せずに)直接入力ファイルを指定することも出来る。[2017-04-30]
import com.example.modelgen.dmdl.csv.SalesDetailCsvFormat;
tester.input(〜).prepare(SalesDetailCsvFormat.class, "sales_detail.csv");
このテストクラスがsrc/test/javaのcom.example.flowpart
パッケージにある場合、
入力ファイルはsrc/test/resources/com/example/flowpartに置く。
Formatクラスを指定するのは、入力ファイルをどういった形式で読み込んで(パースして)どのデータモデルに変換するかを知らせる必要がある為。
入力データが空でいい場合は、prepareメソッドを省略してよい。
ジョブフローの場合 tester.input("salesDetail", SalesDetail.class);
フロー部品の場合 In<SalesDetail> salesDetail = tester.input("salesDetail", SalesDetail.class);
ただし、ジョブフローの場合、Importerで空ファイルを許容するように指定しておかないといけない。
(ImporterクラスでisOptionalメソッドをオーバーライドし、trueを返すようにする)
テストの検証データおよび検証ルールはJobFlowTester/FlowPartTesterのouputメソッド・verifyメソッドで指定する。
ジョブフローの場合 tester.output("categorySummary", CategorySummary.class).verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule");
フロー部品の場合 Out<CategorySummary> categorySummary = tester.output("categorySummary", CategorySummary.class).verify("CategorySummaryFlowPartTest.xls#categorySummary", "CategorySummaryFlowPartTest.xls#categorySummary_rule");
フロー部品の場合はテスト対象FlowPartのコンストラクターに渡す必要があるので、変数に入れておく。
outputメソッドでは、テスト対象クラスの出力ポート名とデータモデルクラスを指定する。
verifyメソッドでは、検証データと検証ルールを指定する。
verifyメソッドでは、検証データと検証ルールを指定する。
Excelの場合はファイル名とシート名を「#」で区切って指定する。
ファイルは、src/test/resourcesの下のテストクラスと同一パッケージの場所に配置しておく。
AsakusaFW 0.9.1以降では、(Excelでデータを記述せずに)直接検証ファイルを指定することも出来る。[2017-04-30]
import com.example.modelgen.dmdl.csv.SalesDetailCsvFormat;
tester.output(〜).verify(SalesDetailCsvFormat.class, "sales_detail.csv", "CategorySummaryFlowPartTest.xls#categorySummary_rule");
このテストクラスがsrc/test/javaのcom.example.flowpart
パッケージにある場合、
検証ファイルはsrc/test/resources/com/example/flowpartに置く。
Formatクラスを指定するのは、検証ファイルをどういった形式で読み込んで(パースして)どのデータモデルに変換するかを知らせる必要がある為。
(検証ファイルを読み込んでデータモデルとして比較するのであり、出力されたファイルをそのまま検証ファイルと比較するわけではない)
出力データを検証する必要が無い場合は、verifyメソッドを省略してよい。
ジョブフローの場合 tester.output("errorRecord", ErrorRecord.class);
フロー部品の場合 Out<ErrorRecord> errorRecord = tester.output("errorRecord", ErrorRecord.class);
出力されたデータの内容を確認したい場合、dumpActualメソッドで出力場所を指定することが出来る。
ジョブフローの場合 tester.output("categorySummary", CategorySummary.class) // .verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule") .dumpActual("file:///tmp/categorySummary.xls");
フロー部品の場合 Out<CategorySummary> categorySummary = tester.output("categorySummary", CategorySummary.class) // .verify("CategorySummaryFlowPartTest.xls#categorySummary", "CategorySummaryFlowPartTest.xls#categorySummary_rule") .dumpActual("file:///tmp/categorySummary.xls");
※dumpActualで指定できるファイルの種類はExcel(拡張子xls)だけっぽい。
実行結果データと検証データの相違内容をファイルに出力することが出来る。
tester.output("categorySummary", CategorySummary.class) .verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule") .dumpDifference("file:///tmp/categorySummary.diff.html");
※相違が無い場合(テストが成功した場合)はこのファイルは出力されない。(ファイルが既存でも削除されたりはしない)
AsakusaFW 0.7.0以降では、検証ルールに従ってチェックする前に、実行結果データを変換することが出来る。
例えば「値がnullだったら0扱いしてチェックしたい」場合に使用する。
import com.asakusafw.testdriver.core.ModelTransformer;
tester.output("categorySummary", CategorySummary.class) .verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule") // .dumpActual("file:///tmp/categorySummary.xls") .transform(new ModelTransformer<CategorySummary>() { @Override public void transform(CategorySummary model) { if (model.getAmountTotalOption().isNull()) { model.setAmountTotal(0); } } });
transformメソッドにはModelTransformerを渡す。
ここには実行結果データが(検証前に)入ってくるので、自由に加工することが出来る。
なお、dumpActualで出力されるデータは、加工前のものになる。
ModelTransformerは関数型インターフェースなので、ラムダ式で記述することが出来る。[2018-09-26]
tester.output("categorySummary", CategorySummary.class) .verify("CategorySummaryJobTest.xls#categorySummary", "CategorySummaryJobTest.xls#categorySummary_rule") // .dumpActual("file:///tmp/categorySummary.xls") .transform(model -> { if (model.getAmountTotalOption().isNull()) { model.setAmountTotal(0); } });
なお、AsakusaFW 0.10.2で、transformはConsumerを受け取るようになった。(Consumerを受け取るメソッドが追加になった)
つまり、Consumerのメソッド(合成の為のandThen)が使える。
(ちなみにModelTransformerはConsumerを継承する形になった)
AsakusaFW 0.10.2で、入力データについても変換できるようになった。[2018-09-26]
(こちらはtransformメソッドではなく、prepareの引数に指定する)
例えば文字列から算出するハッシュ値とか、Excel上で計算するのは大変だが、Java上で計算してセットすることが出来る。
tester.input("hoge", Hoge.class) .prepare("test.xls#hoge", model -> { model.setHashValue(MyHashUtil.calculate(model.getTextAsString())); });
テストデータを記述するExcelファイルの雛形はAsakusaFWによって生成することが出来る。
Shafuの「テストデータ・テンプレートを作成」を実行するとbuild/excelの下にデータモデル毎にExcelファイルが生成されるので、
生成されたExcelファイルから必要なファイルをsrc/test/resourcesの下にコピーし、データや検証ルールを記述する。
入力データや検証データは以下のような感じで記述する。
A | B | C | |
1 | store_code | store_name | |
2 | 0001 | DUMMY | |
3 | 0002 | DUMMY | |
4 | 0003 | DUMMY | |
5 |
一番上がデータモデル内のプロパティー名(ヘッダー行)。
使わないプロパティーは書かなくてもいい(列を削除してよい)し、データモデル内の順番と一致している必要も無い。
AsakusaFW 0.7.0以降では、別のセルを参照したり計算式を書いたりすることが出来る。
検証ルールは以下のような感じで記述する。
A | B | C | D | E | F | |
1 | Format | EVR-2.0.0 | ||||
2 | 全体の比較 | 全てのデータを検査 [Strict] | ||||
3 | プロパティ | 値の比較 | NULLの比較 | コメント | オプション | |
4 | category_code | 検査キー [Key] | 通常比較 [-] | TEXT | ||
5 | amount_total | 完全一致 [=] | 通常比較 [-] | LONG | ||
6 | selling_price_total | 完全一致 [=] | 通常比較 [-] | LONG | ||
7 |
「値の比較」には以下のようなものが選択できる。
値の比較 | ver | 説明 |
---|---|---|
検査対象外 [-] | 1.0.0 | 値の検証(比較)を行わない。 |
検査キー [Key] | 1.0.0 | キーとして使用する。 |
完全一致 [=] | 1.0.0 | 値が完全に一致していることを確認する。 |
部分一致 [<=] | 1.0.0 | |
現在日付 [Today] | 1.0.0 | |
現在時刻 [Now] | 1.0.0 | |
特殊ルール [Expr] | 2.0.0 | 値が範囲内に収まっていることを確認する。範囲は「オプション」で指定する。 |
実行結果データと検証データの比較は、キーが一致したデータ(レコード)に対して行われる。(レコードの並び順は関係ない)
したがって、キー項目を何かしら指定しないといけない。
キーが無いデータ(一意に定まらないデータ)は、AsakusaFWのテストドライバーではテストすることが出来ない。
「値の比較」の「特殊ルール [Expr]」はEVR-2.0.0(AsakusaFW 0.7.0)から使えるようになった機能で、主にDECIMAL・DOUBLE・日付で値が範囲内に収まっていることを確認するもの。
DECIMALやDOUBLEでは、小数点以下の値があると、「完全一致 [=]」では一致と見なされないことがある。この為、特殊ルールで正当な値を範囲指定する。
値そのものは検証データのシートに書き、範囲は「オプション」に記述する。
例えば±0.001の範囲を指定する場合は、オプションに「~0.001
」と書く。
なお、特殊ルールでは、「値がnullであること」を指定することは出来ない。
検証データのシートを空欄にしていると、テスト実行時にエラーになってしまう。(完全一致の比較では、空欄はnullとして扱われる)
→transformを使って値を変換してチェックする
フローのテストを実行するためには、AsakusaFWの実行環境を作っておく必要がある。
$ cd プロジェクト $ ./gradlew installAsakusafw
Windowsでも実行環境を作ることは出来る。
AsakusaFW 0.7.1以降はスモールジョブ実行エンジンを使用する。
AsakusaFW 0.10.0はデフォルトでVanillaが使われるので、特に気にしなくてよい。[2017-12-08]
テストの実行でよく起こるエラーの例。
例外メッセージ | 説明 |
---|---|
java.lang.AssertionError: |
ジョブフローやフロー部品のコンパイルを行う必要がある為、プロジェクトのJavaライブラリーは(JREでなく)JDKである必要がある。 →EclipseのビルドパスのJavaライブラリーをJDKに変更する |
com.asakusafw.lang.compiler.common.DiagnosticException: [/2017-12-02] |
|
java.lang.IllegalStateException:
java.io.IOException: |
環境変数ASAKUSA_HOMEの下にAsakusaFWがインストールされていない。 →実行環境の構築 |
java.lang.AssertionError: |
環境変数ASAKUSA_HOMEの下にインストールされているAsakusaFW(実行環境)のバージョンとプロジェクトのAsakusaFW(開発環境)のバージョンが異なっている。 →実行環境の構築 |
java.lang.IllegalStateException:
java.io.IOException: |
(AsakusaFW 0.10.0より前 [2017-12-08]) Windowsでは、スモールジョブ実行エンジン(AsakusaFW 0.7.1以降)を使わないとテストを実行できない。 →スモールジョブ実行エンジンを使う設定 |
ERROR Failed to locate the winutils binary in the hadoop binary
path |
WindowsのHadoopはwinutils.exeを使うのだが、それが無いとこのメッセージがログに出力される。[2017-02-11] Hadoopのディレクトリーが見つからなくてnullになっているのに、それをチェックせずにコマンドを実行しようとしてエラーになっている(苦笑) ただしAsakusaFWは独自のwinutilsを使うので、テストの実行自体には影響が無い。 |
java.lang.IllegalStateException: ExitCodeException
exitCode=-1073741515: |
Windowsでは、VC++のランタイムが無いと、この例外が発生する。[2017-02-11] →VC++ランタイムエラー |
java.lang.IllegalStateException: java.io.IOException: |
データモデル上でTEXTのプロパティーにExcelデータで数値を指定すると、このエラーになる。 TEXTのプロパティーではデータを文字列にしなければならない。 「2, 3」はエラーのあったデータの位置を表している。 最初の2は2行目、次の3は3列目(C列)を意味している。 |
java.lang.AssertionError: |
実行結果と検証データが一致していない。 →検証エラーメッセージ |
Windows上でフローのテストを実行する際、VC++のランタイムがインストールされていないとエラーになる。[2017-02-11]
java.lang.IllegalStateException: ExitCodeException exitCode=-1073741515: at com.asakusafw.testdriver.FlowPartTester.runTest(FlowPartTester.java:116) 〜 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: ExitCodeException exitCode=-1073741515: at org.apache.hadoop.util.Shell.runCommand(Shell.java:545) 〜 at com.asakusafw.testdriver.FlowPartTester.runTest(FlowPartTester.java:111) ... 24 more
WindowsのHadoopはwinutils.exeというコマンドを使用する。
これを実行するにはVC++のランタイムがインストールされている必要がある。
(大抵の場合は何らかのアプリケーション(Excelとか)をインストールしていればVC++のランタイムもインストールされるのだが、まっさらなWindowsだと入っていないことがある)
AsakusaFWでは、Hadoopがインストールされていなければ独自のwinutils.exeを使う。
この場合は、ログのどこかに以下のようなメッセージが表示されているはず。
11:16:31 INFO installing winutils.exe into default location: C:\Users\HISHID~1\AppData\Local\Temp\winutils-hishidama.exe 11:16:31 INFO winutils.exe is successfully installed: C:\Users\HISHID~1\AppData\Local\Temp\winutils-hishidama.exe
これを直接実行して「MSVCR100.dllがない」というエラーダイアログが出るなら、VC++ランタイムがインストールされていない。
> cd /d C:\Users\HISHID~1\AppData\Local\Temp > winutils-hishidama.exe
> echo %ERRORLEVEL% -1073741515
このエラーを解消するには、Microsoft Visual C++ 2010 再頒布可能パッケージ辺りをインストールすればよい。
実行結果の値が検証データの値と一致しないと例外が発生し、どのプロパティーが一致していなかったか表示される。
java.lang.AssertionError: bid.byCategoryの出力categorySummaryには1個の差異があります: CategorySummary: ["amount-total"(=2)が正しくありません: 「= 1」]: expected <{selling-price-total=100, amount-total=1, category-code="test1"}>, but was <{selling-price-total=100, amount-total=2, category-code="test1"}> at com.asakusafw.testdriver.JobflowExecutor.verify(JobflowExecutor.java:432) at com.asakusafw.testdriver.JobFlowTester.runTestInternal(JobFlowTester.java:167) at com.asakusafw.testdriver.JobFlowTester.runTest(JobFlowTester.java:100) at com.example.jobflow.CategorySummaryJobTest.describe(CategorySummaryJobTest.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
「bid.byCategoryの出力categorySummaryには1個の差異があります
」の「bid.byCategory
」はテスト用のジョブフロー名。
「出力categorySummary
」は出力ポート名。
次の行の最初にある「CategorySummary
」はデータモデルクラス名。
「"amount-total"(=2)が正しくありません: 「= 1」
」は、プロパティーamount_total
の実行結果の値が2、検証データが1であることを表す。
expected
の後ろに出ているのは検証データの値、but was
の後ろに出ているのは実行結果データ。
→dumpDifferenceを使うことで、HTML形式で相違内容を出力できる
検証エラーメッセージに表示されるプロパティー名は、デフォルトではハイフン区切りになっている。
データモデル定義やExcelデータではアンダースコア区切りになっているので、エラーメッセージのプロパティー名をコピペして検索したいときに不便。
で、AsakusaFW 0.7.0で、検証エラーメッセージに使われる区切り文字を指定できるようになった。
import com.asakusafw.testdriver.core.PropertyName;
/** * {@link CategorySummaryJob}のテスト。 */ public class CategorySummaryJobTest { static { System.setProperty(PropertyName.KEY_SEGMENT_SEPARATOR, "_"); } 〜 }
これで、検証エラーメッセージに表示されるプロパティー名がハイフン区切りになる。
↓
java.lang.AssertionError: bid.byCategoryの出力categorySummaryには1個の差異があります: CategorySummary: ["amount_total"(=2)が正しくありません: 「= 1」]: expected <{selling_price_total=100, amount_total=1, category_code="test1"}>, but was <{selling_price_total=100, amount_total=2, category_code="test1"}>
フローのテストを実行した時に、AsakusaFWによって生成されたクラスで例外が発生することがある。[2015-04-18]
フローのテスト時に生成されるソースは(デフォルトでは)“Javaの一時ディレクトリー”上に作られる為、探すのが大変。
だが、以下のようにすれば探すのが楽になる。
FlowPartTester tester = new FlowPartTester(getClass());
tester.setCompilerWorkingDirectory(new File("build/tmp/flowTest"));
-Dasakusa.testdriver.compilerwork.dir=build/tmp/flowTest
」を指定する。java.lang.NullPointerException: null at com.asakusafw.runtime.value.DecimalOption.get(DecimalOption.java:84) ~[asakusa-runtime-0.7-SNAPSHOT.jar:na] at test.flowpart.testing.flowpart.stage0001.MapOutputFragment1.add(MapOutputFragment1.java:24) ~[jobflow-flowpart.jar:na] at test.flowpart.testing.flowpart.stage0001.MapOutputFragment1.add(MapOutputFragment1.java:10) ~[jobflow-flowpart.jar:na] at test.flowpart.testing.flowpart.stage0001.MapFragment1.add(MapFragment1.java:19) ~[jobflow-flowpart.jar:na] at test.flowpart.testing.flowpart.stage0001.StageMapper1.runInternal(StageMapper1.java:26) ~[jobflow-flowpart.jar:na] 〜
フローのテストクラス実行用の「Run Configurations」の「Source」タブの「Source Lookup
Path」に、追加したソースパスが表示されている。
逆に言えば、ここからソースパスを追加しておくことでも同様の効果が得られる。
(あるいは、不要になったソースパスを削除することが出来る)
→(DMDL EditorXの)スタックトレースからファイルを検索する機能(実行環境で例外が発生したときのソース検索)