这是我遇到的错误的堆栈跟踪:
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(); }
解决方法
那么在代码库中如何发生.注意你在嘲笑这两个类
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和测试代码中的mock nodeAutoIndexer都具有AutoIndexer&Node Node类型的引用,因此关于多态调度没有问题.
但是,您的生产代码中的relAutoIndex由AutoIndexer&Relationships类型引用.并且您的测试代码中的模拟relAutoIndexer由RelationshipAutoIndexer引用,因此错误的调用在模拟器上注册,然后验证失败.
您的解决方案是升级mockito版本; 1.9.0 RC1是非常稳定的,最后一个版本应该是你的方式.或者您可以从以下地址迁移参考类型(在生产代码中):
AutoIndexer<Relationship> relAutoIndexer = this.graphDb.index().getRelationshipAutoIndexer();
至 :
RelationshipAutoIndexer relAutoIndexer = this.graphDb.index().getRelationshipAutoIndexer();
另外几句话.
>你不需要在这里写一个after方法,因为JUnit在每个方法运行时创建一个新的实例,所以你的方法只是添加将要完成的代码.请注意,TestNG不是这样.
而不是在before方法中创建你的mocks,你可能想使用Mockito注释.不要忘记跑步者.
例如 :
@RunWith(MockitoJUnitRunner.class) public class YourTest { @Mock SomeType someTypeMock; // ... }
>由于几个原因,stubbing代码有点丑陋.
>你应该写一个一致的存根.
为什么不用清洁的方式写这个;例如在两种情况下引用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);