我正在为我的java应用程序编写单元测试,该应用程序分析对Stash的提交,Stash是一个类似于Github的atlassian应用程序。
我正在测试的方法就是这个:
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 notes commit,则忽略处理并调用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
相反,我得到:
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.findCommitInfo(..)
的调用默认情况下,当模拟调用时,带有void签名的方法不执行任何操作。这正是我想要它做的。我希望它被调用但不做任何事情,只记录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 :(得分:0)
从我在设置中看到的内容,sepCommits
是类中的依赖项,包含processEvent()
方法。
您必须在sepRefChangeEvent
变量中注入您在测试中创建的模拟。通常,这可以通过在构造期间将其作为参数传递或通过setter方法来完成。我在你的测试类中看不到这样的代码。我认为你实际上是在击中一个真实的实例,而不是那里的模拟实例,这导致异常。
答案 1 :(得分:0)
我认为Mockito间谍不是一种自然的行为,因为你的课程既是模拟的,也是考试的阶级。
在遗留代码中,它可能是可以接受的,但在新代码中,它很可惜。
此外,在您的情况下,我认为您不需要间谍活动。
您的逻辑是:如果ref name
输入与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);
}