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();
}
Mockitoは、バージョン1.8.5まで、多形ディスパッチの場合にバグがありました。これは修正され、バージョン1.9.0の最初のリリース候補で利用可能です。issue 2 を参照してください。
では、コードベースでどのように発生するのでしょうか。これらの2つのクラスをモックしていることに注意してください
_nodeAutoIndexer = (AutoIndexer<Node>) mock(AutoIndexer.class);
relAutoIndexer = mock(RelationshipAutoIndexer.class);
_
AutoIndexer
はたまたま一般的な親インターフェースであり、このインターフェースにはこのメソッドReadableIndex<T> getAutoIndex()
があります。 RelationshipAutoIndexer
はAutoInexer
のサブタイプであり、ジェネリック部分はRelationship
に固定され、getAutoIndex()
メソッドをオーバーライドして共変型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
とテストコードのモックnodeAutoIndexer
の両方にタイプ_AutoIndexer<Node>
_の参照があるため、ポリモーフィックディスパッチに関して問題はありません。ただし、本番コードのrelAutoIndex
はタイプ_AutoIndexer<Relationship>
_で参照され、テストコードのモックrelAutoIndexer
はタイプRelationshipAutoIndexer
で参照されるため、間違った呼び出しが行われます。モックに登録された後、検証に失敗します。
あなたの解決策は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);
_