2016-08-15 12 views
4

私はGithubに似たatlassianアプリStashにコミットを分析する私のjava appの単体テストを書いています。voidメソッドが擬似オブジェクトでNullPointerExceptionを取得するのはなぜですか?

私がテストしていメソッドは、このいずれかになります。

public List<Message> processEvent(RepositoryRefsChangedEvent event) { 
    ArrayList<Message> commitList = new ArrayList<Message>(); 

    for (RefChange refChange : event.getRefChanges()) { 
     LOGGER.info("checking ref change refId={} fromHash={} toHash={} type={}", refChange.getRefId(), refChange.getFromHash(), 
       refChange.getToHash(), refChange.getType()); 

     if (refChange.getRefId().startsWith(REF_BRANCH)) { 
      if (refChange.getType() == RefChangeType.ADD && isDeleted(refChange)) { 
       LOGGER.info("Deleted a ref that never existed. This shouldn't ever occur."); 
      } 
      else if (isDeleted(refChange) || isCreated(refChange)) { 
       branchCreation(refChange, event.getRepository(), commitList); 
      } 
      else { 
       sepCommits.findCommitInfo(refChange, event.getRepository(), commitList); 
      } 
     } 
     else { 
      refNotProcessed(refChange); 
     } 
    } 
    return commitList; 
} 

私はGitのノートがコミットしている場合、処理は無視され、refNotProcessed(..)が呼び出されていることを確認しようとしています。

幸いにも、私は比較的容易にこれを理解することができたし、下のソリューションに来た:私が変更された場合は、この後

@RunWith (MockitoJUnitRunner.class) 
public class RefChangEventTest { 
    @Mock RefChange ref; 
    @Mock RepositoryRefsChangedEvent refsChangedEvent; 
    @Mock Repository repo; 
    @Mock ApplicationPropertiesService appService; 
    @Mock SEPCommits sepCommits; 
    @Spy SEPRefChangeEventImpl sepRefChangeEvent = new SEPRefChangeEventImpl(sepCommits, appService); 

    @Before 
    public void testSetup() { 
     Collection<RefChange> refList = new ArrayList<RefChange>(1); 
     refList.add(ref); 
     when(refsChangedEvent.getRefChanges()).thenReturn(refList); 
     when(refsChangedEvent.getRepository()).thenReturn(repo); 
    } 

    @Test 
    public void gitNotesAreIgnored() throws Exception { 
     when(ref.getRefId()).thenReturn("refs/notes/foo"); 
     when(ref.getFromHash()).thenReturn("da69d7e202d7f66cba01c6f4030bd5975adbf200"); 
     when(ref.getToHash()).thenReturn("da69d7e202d7f66cba01c6f4030bd5975adbf201"); 
     doNothing().when(sepCommits).findCommitInfo(any(RefChange.class), any(Repository.class), any(ArrayList.class)); 

     sepRefChangeEvent.processEvent(refsChangedEvent); 
     verify(sepRefChangeEvent, times(1)).refNotProcessed(ref); 
    } 

、私は私のユニットテストは、右の理由で失敗するかどうかを確認したかったです参照名はrefs/heads/fooのようになります。代わりに私が手expected 1 execution of refNotProcessed but was not run at all

:モックがそれらを呼び出したときにデフォルトでsepCommits.findCommitInfo(..)への呼び出しを指す

java.lang.NullPointerException 
at com.cray.stash.SEPRefChangeEventImpl.processEvent(SEPRefChangeEventImpl.java:62) 
at ut.com.isroot.stash.plugin.RefChangEventTest.gitNotesAreIgnored(RefChangEventTest.java:48) 
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:497) 
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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 
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.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37) 
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) 
at org.junit.runner.JUnitCore.run(JUnitCore.java:160) 
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) 
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) 
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253) 
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) 
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:497) 
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 

は、無効シグネチャを持つメソッドは何もしない、私はの線に沿って何かを見たいと思います。それはまさに私がしたいことです。私はそれが呼び出されたいが、sepCommitsがやりとりされたという事実を記録するだけです。 NPEはなぜ起こっていますか?ここで

は、人々が求めているいくつかのより多くのメソッドです:私はあなたのセットアップ中に表示されるものと

public SEPRefChangeEventImpl(SEPCommits sepCommits, ApplicationPropertiesService appService) { 
    this.sepCommits = sepCommits; 

    try { 
     endpoint = appService.getPluginProperty("plugin.fedmsg.events.relay.endpoint"); 
    } catch (Exception e) { 
     LOGGER.error("Failed to retrieve properties\n" + e); 
    } 

    if (endpoint == null) { 
     endpoint = "tcp://some.web.address" 
    } 
} 


public void refNotProcessed(RefChange refChange) { 
    LOGGER.info("This type of refChange is not supported.\n refId={} fromHash={} toHash={} type={}", refChange.getRefId(), refChange.getFromHash(), 
      refChange.getToHash(), refChange.getType()); 
} 

public void findCommitInfo(RefChange ref, Repository repo, ArrayList<Message> commitList) { 
    Page<Commit> commits = getChangeset(repo, ref); 
    for (Commit commit : commits.getValues()) { 
     String topic = topicPrefix + repo.getProject().getKey() + "." + repo.getName() + ".commit"; 
     Message message = new Message(getInfo(commit, ref), topic); 
     commitList.add(message); 
    } 
} 
+0

コードに問題はありませんが、私は好きではありません。あなたは使用されているすべてのメソッド/フィールド/コンストラクタを表示できますか? – davidxxx

+0

sepRefChangeEventの検証を実行するにはスパイが必要です。実行するにはモックが必要なためです。私は参照のためのより多くのメソッドを追加しました! –

+0

私は特別なことは見ませんでした。スパイすることなく試してみることができますか? – davidxxx

答えて

0

sepCommitsprocessEvent()方法を含む、クラスの依存関係です。

sepRefChangeEvent変数にテストで作成しているモックを注入する必要があります。通常、これは、構築中にパラメータとして渡すか、setterメソッドを介して渡します。あなたのテストクラスにそのようなコードはありません。私は実際にあなたが実際のインスタンスに当たっていると思うが、そこに嘲笑されたものではなく、これが例外を引き起こしていると思う。

+0

私は 'sepRefChangeEvent'変数をインスタンス化するときに、最初のパラメータに' sepCommits'を渡します。 –

+0

ああ、申し訳ありませんが、それを逃してしまったのでしょうか?しかし、なぜあなたはあなたのテストされたインスタンスを監視していますか?テストされたクラスの通常のインスタンスを使用しているだけの場合はどうなりますか? –

+0

心配はいりません!通常のインスタンスとして宣言した場合、verify(sepRefChangeEvent、times(1))。refNotProcessed(ref) 'で検証を行うにはモックが必要です。 –

0

あなたのクラスはモックとテストクラスの両方であるため、モッキートスパイは自然な動作ではないと思います。
レガシーコードでは、それは受け入れられるかもしれませんが、新しいコードでは残念です。

さらに、あなたの場合、私はあなたがスパイをする必要はないと思います。
あなたのロジックは:入力がREF_BRANCHと一致する場合は、何も処理しません。したがって、受け入れたテストでは処理を行い(検証モックは呼び出されます)、受け入れられないテストでは処理を実行しないことを確認する必要があります(モックが呼び出されていないことを確認してください)。

 // You do the processing 
     if (refChange.getRefId().startsWith(REF_BRANCH)) { 
      if (refChange.getType() == RefChangeType.ADD && isDeleted(refChange)) { 
       LOGGER.info("Deleted a ref that never existed. This shouldn't ever occur."); 
      } 
      else if (isDeleted(refChange) || isCreated(refChange)) { 
       branchCreation(refChange, event.getRepository(), commitList); 
      } 
      else { 
       sepCommits.findCommitInfo(refChange, event.getRepository(), commitList); 
      } 
     } 
     // You do nothing 
     else { 
      refNotProcessed(refChange); 
     } 

テストされたクラスの別のパブリックメソッドを受諾テストと呼びますが受け入れテストではないことをアサートします。

実際にテストしたい場合は、public method refNotProcessedをその内部に移動する新しいクラスを導入する別の解決策があります。 あなたは、処理クラスの処理方法に入力し、派遣中の流れを制御するために、このクラスを保つことができる:

 // You do the processing 
     if (refChange.getRefId().startsWith(REF_BRANCH)) { 
      if (refChange.getType() == RefChangeType.ADD && isDeleted(refChange)) { 
       LOGGER.info("Deleted a ref that never existed. This shouldn't ever occur."); 
      } 
      else if (isDeleted(refChange) || isCreated(refChange)) { 
       processClass.branchCreation(refChange, event.getRepository(), commitList); 
      } 
      else { 
       sepCommits.findCommitInfo(refChange, event.getRepository(), commitList); 
      } 
     } 
     // You do nothing 
     else { 
      processClass.refNotProcessed(refChange); 
     } 
+0

これは良いアドバイスのようですが、おそらく私の問題は解決しません。 'refNotProcessed'と' branchCreation'を別のクラスに入れるように言っています。私はまだ同じ問題を抱えています。 –

+0

私はすべてここでメッセージに交差しないように答えます。 いいえ、私はスパイ注釈を削除することを提案しますが、Mockと置き換えることはしません。自然のものとして使用してください。私はスパイの使用を止めれば、 'processClass'を模倣する問題はなくなると思います。 – davidxxx

+0

私のテストでは、検証のために' verify'が使われています。 'verify'メソッドはモックオブジェクトを扱う必要があります。私はnon-mock sepRefChangeEvent変数を使用することはできません。私は "NotAMockException"を取得します。私がその変数について検証を実行したいのであれば、それは疑似である必要があります。 –

0

これは私のソリューションです:

@RunWith (MockitoJUnitRunner.class) 
public class RefChangEventTest { 
    @Mock RefChange ref; 
    @Mock RepositoryRefsChangedEvent refsChangedEvent; 
    @Mock ApplicationPropertiesService appService; 
    @Mock ArrayList<Message> mockList; 
    @Mock RepositoryService repositoryService; 
    @Mock RefService refService; 
    @Mock CommitService commitService; 
    @Mock SecurityService securityService; 
    @Mock Repository repo; 

    SEPCommits sepCommits = mock(SEPCommits.class, RETURNS_DEEP_STUBS); 
    @Spy SEPRefChangeEventImpl sepRefChangeEvent = new SEPRefChangeEventImpl(sepCommits, appService); 

    @Before 
    public void testSetup() { 
     Collection<RefChange> refList = new ArrayList<RefChange>(1); 
     refList.add(ref); 
     when(refsChangedEvent.getRefChanges()).thenReturn(refList); 
     when(refsChangedEvent.getRepository()).thenReturn(repo); 
    } 

    @Test 
    public void gitNotesAreIgnored() throws Exception { 
     when(ref.getRefId()).thenReturn("refs/notes/foo"); 
     when(ref.getFromHash()).thenReturn("da69d7e202d7f66cba01c6f4030bd5975adbf200"); 
     when(ref.getToHash()).thenReturn("da69d7e202d7f66cba01c6f4030bd5975adbf201"); 

     sepRefChangeEvent.processEvent(refsChangedEvent); 
     verifyZeroInteractions(sepCommits); 
    } 

差は、sepCommitsが嘲笑される方法でありますそれはRETURN_DEEP_STUBSです。私がsepCommitsを完全に初期化するのを避ける理由は、findCommitInfo(..)の中にあるたくさんのメソッドを気絶させる必要があるからです。私はそのメソッドが呼び出されるかどうかを確認したいだけです。 RETURN_DEEP_STUBSが良い解決策であるかどうかはわかりませんが、それは私のために働いています。

関連する問題