JBehaveのGivenStoriesでExamples:の顛末




前回@なんかの特殊文字をエスケープするのに
Examples:を使用して対応した件の続き。

実はこのstoryをGivenStoriesとして実行したかったのに
Examples:はGivenStoriesで実行するとなぜかこける！
これまた困った。。。

```
|id|pass|
|xxx@domain.com|hogehoge|
Failed to run story scenario/xxx.story
java.lang.NullPointerException
	at de.codecentric.jbehave.junit.monitoring.JUnitScenarioReporter.example(JUnitScenarioReporter.java:197)
	at org.jbehave.core.reporters.DelegatingStoryReporter.example(DelegatingStoryReporter.java:91)
	at org.jbehave.core.reporters.ConcurrentStoryReporter.example(ConcurrentStoryReporter.java:209)
	at org.jbehave.core.embedder.StoryRunner.runScenariosParametrisedByExamples(StoryRunner.java:429)
	at org.jbehave.core.embedder.StoryRunner.runCancellable(StoryRunner.java:303)
	at org.jbehave.core.embedder.StoryRunner.run(StoryRunner.java:220)
	at org.jbehave.core.embedder.StoryRunner.runGivenStories(StoryRunner.java:389)
	at org.jbehave.core.embedder.StoryRunner.runCancellable(StoryRunner.java:272)
	at org.jbehave.core.embedder.StoryRunner.run(StoryRunner.java:220)
	at org.jbehave.core.embedder.StoryRunner.run(StoryRunner.java:181)
	at org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:235)
	at org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:207)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:92)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:58)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:37)
	at org.jbehave.core.embedder.StoryManager.submit(StoryManager.java:204)
	at org.jbehave.core.embedder.StoryManager.runningStory(StoryManager.java:132)
	at org.jbehave.core.embedder.StoryManager.filterRunning(StoryManager.java:116)
	at org.jbehave.core.embedder.StoryManager.runningStoriesAsPaths(StoryManager.java:101)
	at org.jbehave.core.embedder.StoryManager.runStories(StoryManager.java:78)
	at org.jbehave.core.embedder.Embedder.runStoriesAsPaths(Embedder.java:203)
	at de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner.run(JUnitReportingRunner.java:78)
	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:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
```


#というわけでいろいろ調べてみた


同じような問題抱えてる人がちょくちょくいるみたいで
以下のページでは回答として4.0bだと
「The JBehave-core 4.0-beta-9 seems to work just fine」
うまく動いてるように見えるよーとのこと。
http://stackoverflow.com/questions/25589963/jbehave-junit-runner-throwing-nullpointerexception

現在当方のJBehaveのバージョンは3.9.3。
とりあえずバージョン上げて試してみることに。

結果は以下の通り。
3.9.4⇒×（エラーは3.9.3と同じ）
4.0-beta-9⇒×（エラーの出方変わったな）
4.0-beta-10⇒×（上に同じ）

```
Failed to run story scenario/xxx.story
Example: {id=xxx@domain.com, pass=hogehoge}
Failed to run story scenario/xxx.story
org.jbehave.core.failures.UUIDExceptionWrapper: java.lang.NullPointerException
	at org.jbehave.core.embedder.PerformableTree.perform(PerformableTree.java:366)
	at org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:265)
	at org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:241)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:92)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:58)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:37)
	at org.jbehave.core.embedder.StoryManager.submit(StoryManager.java:238)
	at org.jbehave.core.embedder.StoryManager.runningStory(StoryManager.java:155)
	at org.jbehave.core.embedder.StoryManager.filterRunning(StoryManager.java:139)
	at org.jbehave.core.embedder.StoryManager.runningStories(StoryManager.java:131)
	at org.jbehave.core.embedder.StoryManager.performStories(StoryManager.java:115)
	at org.jbehave.core.embedder.StoryManager.runStories(StoryManager.java:102)
	at org.jbehave.core.embedder.StoryManager.runStoriesAsPaths(StoryManager.java:81)
	at org.jbehave.core.embedder.Embedder.runStoriesAsPaths(Embedder.java:204)
	at de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner.run(JUnitReportingRunner.java:78)
	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:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.NullPointerException
	at de.codecentric.jbehave.junit.monitoring.JUnitScenarioReporter.example(JUnitScenarioReporter.java:197)
	at org.jbehave.core.reporters.DelegatingStoryReporter.example(DelegatingStoryReporter.java:91)
	at org.jbehave.core.reporters.ConcurrentStoryReporter.example(ConcurrentStoryReporter.java:210)
	at org.jbehave.core.embedder.PerformableTree$ExamplePerformableScenario.perform(PerformableTree.java:903)
	at org.jbehave.core.embedder.PerformableTree$PerformableScenario.perform(PerformableTree.java:814)
	at org.jbehave.core.embedder.PerformableTree$PerformableStory.performScenarios(PerformableTree.java:747)
	at org.jbehave.core.embedder.PerformableTree$PerformableStory.perform(PerformableTree.java:726)
	at org.jbehave.core.embedder.PerformableTree$PerformableStory.performGivenStories(PerformableTree.java:740)
	at org.jbehave.core.embedder.PerformableTree$PerformableStory.perform(PerformableTree.java:725)
	at org.jbehave.core.embedder.PerformableTree.performCancellable(PerformableTree.java:380)
	at org.jbehave.core.embedder.PerformableTree.perform(PerformableTree.java:360)
	... 23 more
```



うん、全く動きません。

それよりも釈然としないのが、何でNullPointerException？？？
でもパラメータ見る限りExamples自体は取れてる模様。

とりあえずエラーの起点になってるJUnitScenarioReporterを確認してみる。


```java:JUnitScenarioReporter.java

L.198~

public void example(Map<String, String> arg0) {
	logger.info("Example: {}", arg0);

	stepDescriptions = nextExample.getChildren().iterator(); // nextExampleがnull
	if (stepDescriptions.hasNext()) {
		currentStep = stepDescriptions.next();
	}

	if (exampleDescriptions.hasNext()) {
		nextExample = exampleDescriptions.next();
	}

}
```

nullだったのはnextExample。じゃsetしている箇所はというと


```java:JUnitScenarioReporter.java

L.128~

public void beforeScenario(String title) {
	logger.info("Before Scenario: {}", title);
	if (!givenStoryContext) { // ←え、何これ。
		notifier.fireTestStarted(currentScenario);

		ArrayList<Description> children = currentScenario.getChildren();
		List<Description> examples = filterExamples(children);
		if (!examples.isEmpty()) {
			exampleDescriptions = examples.iterator();
			if (exampleDescriptions.hasNext()) {
				nextExample = exampleDescriptions.next(); // ここでsetなんだが…
			}
		}
		if (children.size() > examples.size()) {
			// in case of given stories, these steps are actually stories,
			// for which events
			// will be fired in beforeStory(..., true)
			ArrayList<Description> steps = new ArrayList<Description>(
					currentScenario.getChildren());
			steps.removeAll(examples);
			stepDescriptions = getAllDescendants(steps).iterator();
			if (stepDescriptions.hasNext()) {
				currentStep = stepDescriptions.next();
			}
		}
	}
}

```


つまりgivenStoriesではExamplesの中身を展開しません、ってことですね。。。

これは憶測だけど、
GivenStoriesとメインのStoryでExamples設定が重複すると不都合があるから
GivenStories側は殺してるんじゃないかなーとか。


#ここで二者択一を迫られる
①諦めてメインのstoryに書いちゃう
②別の方法を模索する

あ、嘘。もう一個あった。
③Examples以外の別のエスケープを模索する



今回は時間が無いため、①で対応することに。
時間をみてリファクトしてみよう。