15

関数が Mockito を使用して呼び出されたことを確認していますが、Mockito は、確認している関数が呼び出されたことはなく、他の関数が呼び出されたことを示しています。しかし、私は正しい関数を呼び出しているようです...

私が得ているエラーのスタックトレースは次のとおりです。

Wanted but not invoked:
relationshipAutoIndexer.getAutoIndex();
-> at org.whispercomm.manes.server.graph.DataServiceImplTest.testInitIndices(DataServiceImplTest.java:117)

However, there were other interactions with this mock:
-> at org.whispercomm.manes.server.graph.DataServiceImpl.updateIndexProperties(DataServiceImpl.java:136)
-> at org.whispercomm.manes.server.graph.DataServiceImpl.updateIndexProperties(DataServiceImpl.java:144)
-> at org.whispercomm.manes.server.graph.DataServiceImpl.updateIndexProperties(DataServiceImpl.java:148)
-> at org.whispercomm.manes.server.graph.DataServiceImpl.updateIndexProperties(DataServiceImpl.java:149)
-> at org.whispercomm.manes.server.graph.DataServiceImpl.initIndices(DataServiceImpl.java:121)

    at org.whispercomm.manes.server.graph.DataServiceImplTest.testInitIndices(DataServiceImplTest.java:117)

それはで発生します

verify(relAutoIndexer).getAutoIndex();

以下に示すテストクラスコードの。

これが私のコードです(私は偶然に物事を省く傾向があります。不足していると思われるコードを尋ねてください。追加します):

public DataServiceImpl(GraphDatabaseService graphDb) {
    super();
    this.graphDb = graphDb;
    unarchivedParent = new UnarchivedParent(graphDb.createNode());
    archivedParent = new ArchivedParent(graphDb.createNode());
    packetParent = new PacketParent(graphDb.createNode());
    userParent = new UserParent(graphDb.createNode());
    this.initIndices();
}

/**
 * Initializes the node and relationship indexes.
 * 
 * Updates the set of indexed properties to match {@link DataServiceImpl}
 * .NODE_KEYS_INDEXABLE and {@link DataServiceImpl}.REL_KEYS_INDEXABLE.
 * 
 * Note: auto indices can also be configured at database creation time and
 * just retrieved at runtime. We might want to switch to that later.
 */
private void initIndices() {
    /* Get the auto-indexers */
    AutoIndexer<Node> nodeAutoIndexer = this.graphDb.index()
            .getNodeAutoIndexer();

    AutoIndexer<Relationship> relAutoIndexer = this.graphDb.index()
            .getRelationshipAutoIndexer();

    this.updateIndexProperties(nodeAutoIndexer,
            DataServiceImpl.NODE_KEYS_INDEXABLE);

    this.nodeIndex = nodeAutoIndexer.getAutoIndex();

    this.updateIndexProperties(relAutoIndexer,
            DataServiceImpl.REL_KEYS_INDEXABLE);

    this.relIndex = relAutoIndexer.getAutoIndex();
}

/**
 * Sets the indexed properties of an {@link AutoIndexer} to the specified
 * set, removing old properties and adding new ones.
 * 
 * @param autoIndexer
 *            the AutoIndexer to update.
 * @param properties
 *            the properties to be indexed.
 * @return autoIndexer, this given AutoIndexer (useful for chaining calls.)
 */
private <T extends PropertyContainer> AutoIndexer<T> updateIndexProperties(
        AutoIndexer<T> autoIndexer, Set<String> properties) {
    Set<String> indexedProps = autoIndexer.getAutoIndexedProperties();
    // Remove unneeded properties.
    for (String prop : difference(indexedProps, properties)) {
        autoIndexer.stopAutoIndexingProperty(prop);
    }

    // Add new properties.
    for (String prop : difference(properties, indexedProps)) {
        autoIndexer.startAutoIndexingProperty(prop);
    }

    // Enable the index, if needed.
    if (!autoIndexer.isEnabled()) {
        autoIndexer.setEnabled(true);
    }

    return autoIndexer;
}

テストクラスのコードは次のとおりです。

@Before
public void setup() {
   nA = mock(Node.class);
   nB = mock(Node.class);
   packetA = new PacketWrapper(nA);
   packetB = new PacketWrapper(nB);
   RelA = mock(Relationship.class);
   RelB = mock(Relationship.class);
   graphDb = mock(GraphDatabaseService.class);
   nodeAutoIndexer = (AutoIndexer<Node>) mock(AutoIndexer.class);
       relAutoIndexer = mock(RelationshipAutoIndexer.class);
}

@After
public void tearDown() {
  packetA = null;
  packetB = null;
}
/*
 * ---------------- Test initIndices() ---------------
 */
//TODO
@Test
public void testInitIndices() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
   IndexManager indexManager = mock(IndexManager.class);
   when(graphDb.index()).thenReturn(indexManager);
   when(indexManager.getNodeAutoIndexer()).thenReturn(nodeAutoIndexer);
       when(graphDb.index().getRelationshipAutoIndexer()).thenReturn(relAutoIndexer);
   dataService = new DataServiceImpl(graphDb);
       verify(nodeAutoIndexer, atLeastOnce()).getAutoIndex();
       verify(relAutoIndexer).getAutoIndex();                       
}
4

1 に答える 1

26

Mockitoは、バージョン1.8.5まで、ポリモーフィックディスパッチの場合にバグがありました。これは修正され、バージョン1.9.0の最初のリリース候補で利用可能になりました。問題200を参照してください。

では、コードベースでどのように発生するのでしょうか。これらの2つのクラスをモックしていることに注意してください

nodeAutoIndexer = (AutoIndexer<Node>) mock(AutoIndexer.class);
relAutoIndexer = mock(RelationshipAutoIndexer.class);

AutoIndexerたまたま一般的な親インターフェースであり、このインターフェースにはこのメソッドがあります ReadableIndex<T> getAutoIndex()。は、ジェネリック部分がに固定されている場所のRelationshipAutoIndexerサブタイプであり、メソッドをオーバーライドして共変タイプを返します。AutoInexerRelationshipgetAutoIndex()ReadableRelationshipIndex

AutoIndexerおよびRelationshipIndexerを参照してください。

さて、あなたの呼び出しコードにはこれらの行があります:

AutoIndexer<Node> nodeAutoIndexer = this.graphDb.index().getNodeAutoIndexer();
AutoIndexer<Relationship> relAutoIndexer = this.graphDb.index().getRelationshipAutoIndexer();
this.nodeIndex = nodeAutoIndexer.getAutoIndex();
this.relIndex = relAutoIndexer.getAutoIndex();

本番コードとテストコードnodeAutoIndexのモックの両方にタイプの参照があるため、ポリモーフィックディスパッチに関して問題はありません。ただし、本番コードではタイプによって参照され、テストコードのモックはタイプによって参照されるため、間違った呼び出しがモックに登録され、検証に失敗します。nodeAutoIndexerAutoIndexer<Node>relAutoIndexAutoIndexer<Relationship>relAutoIndexerRelationshipAutoIndexer

あなたの解決策は、mockitoバージョンをアップグレードすることです。1.9.0 RC1は非常に安定しており、最終リリースが近づいています。または、参照型(本番コード内)を次の場所から移行できます。

AutoIndexer<Relationship> relAutoIndexer = this.graphDb.index().getRelationshipAutoIndexer();

に :

RelationshipAutoIndexer relAutoIndexer = this.graphDb.index().getRelationshipAutoIndexer();

他のいくつかの発言。

  • JUnitはメソッドの実行ごとに新しいインスタンスを作成するため、実際にはここにafterメソッドを記述する必要はありません。したがって、メソッドはとにかく実行されるコードを追加するだけです。これはTestNGには当てはまらないことに注意してください。

  • beforeメソッドでモックを作成する代わりに、Mockitoアノテーションを使用することをお勧めします。ランナーを忘れないでください。

例えば ​​:

@RunWith(MockitoJUnitRunner.class)
public class YourTest {
    @Mock SomeType someTypeMock;
    // ...
}
  • スタブコードは、いくつかの理由で少し醜いです。

    • 一貫性のあるスタブを作成する必要があります。

これをもっときれいな方法で書いてみませんか。たとえばindexManager、両方の場合の参照:

IndexManager indexManager = mock(IndexManager.class);
when(graphDb.index()).thenReturn(indexManager);
when(indexManager.getNodeAutoIndexer()).thenReturn(nodeAutoIndexer);
when(indexManager.getRelationshipAutoIndexer()).thenReturn(relAutoIndexer);

または、まったく参照しないでください

IndexManager indexManager = mock(IndexManager.class);
when(graphDb.index()).thenReturn(indexManager);
when(graphDb.index().getNodeAutoIndexer()).thenReturn(nodeAutoIndexer);
when(graphDb.index().getRelationshipAutoIndexer()).thenReturn(relAutoIndexer);

また、モックを返すモックを持っていることは、通常、デザインのにおいの兆候です。あなたはデメテルの法則を破っています、そしてそれを破ることはあなたが困難なテスト、悪い保守性、そして困難な進化を経験することを意味します。私がささやくのを聞くことができると私が言うとき(三段論法なしで):それはあなたにお金がかかるでしょう。レガシーコードを書かないでください!TDDまたはBDDを実践している場合は、設計時にこれらの問題を独自のコードで特定します。これは、これらの問題を防ぐのに最適です。

  • ただし、レガシーコードを扱っている場合は、次のディープスタブ構文を使用できます。

静的メソッドを使用すると、これを書くことができます

GraphDatabaseService graphdb = mock(GraphDatabaseService.class, RETURNS_DEEP_STUBS);

または、注釈を使用してこれを書くことができます:

@Mock(answer = RETURNS_DEEP_STUBS) GraphDatabaseService graphdb;

そしてスタブ:

when(graphDb.index().getNodeAutoIndexer()).thenReturn(nodeAutoIndexer);
when(graphDb.index().getRelationshipAutoIndexer()).thenReturn(relAutoIndexer);
于 2011-10-31T14:26:35.210 に答える