为什么ArgumentCaptor没有正确匹配?

flo*_*cca 7 java android mockito

我正在开发Android应用程序,使用AndroidStudio,我希望有人可以告诉我为什么我不能让Mockito使用argumentCaptor.capture()或识别参数anyObject().

我正在测试SpanPainter的方法applyColor():

package com.olfybsppa.inglesaventurero.utils;

import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;

public class SpanPainter {
  ForegroundColorSpan color;

  public SpanPainter (ForegroundColorSpan color) {
    this.color = color;
  }
  public SpannableString applyColor(SpannableString span) {
    span.setSpan(color, 1, 2, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
    return span;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的测试是:

package com.olfybsppa.inglesaventurero.utils;

import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class SpanPainterTest {
  @Test
  public void testAppliesColorPerRange () {
    SpannableString mockSpanString = mock(SpannableString.class);
    ForegroundColorSpan mockForegroundColor = mock(ForegroundColorSpan.class);
    SpanPainter painter = new SpanPainter(mockForegroundColor);

    ArgumentCaptor<ForegroundColorSpan> argumentCaptor = ArgumentCaptor.forClass(ForegroundColorSpan.class);
    painter.applyColor(mockSpanString);

    verify(mockSpanString).setSpan(argumentCaptor.capture(), anyInt(), anyInt(), anyInt());
    //verify(mockSpanString).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
  }
}
Run Code Online (Sandbox Code Playgroud)

结果是:(我删除了尖括号)

Argument(s) are different! Wanted:
spannableString.setSpan(
    Capturing argument,
    any,
    any,
    any
);
Actual invocation has different arguments:
spannableString.setSpan(
    Mock for ForegroundColorSpan, hashCode: 106298691,
    1,
    2,
    17
);
Run Code Online (Sandbox Code Playgroud)

如果我删除注释行并使用anyObject(),结果是:

Argument(s) are different! Wanted:
spannableString.setSpan(any,any,any,any);
Actual invocation has different arguments:
spannableString.setSpan(
    Mock for ForegroundColorSpan, hashCode: 106298691,
    1,
    2,
    17
);
Run Code Online (Sandbox Code Playgroud)

在我看来,至少使用anyObject()应该工作,但事实并非如此.

不再引用我的主代码,仍然使用Android api对象

下面的代码给出了类似的结果,'参数是不同的!想要......''捕获参数'vs'模拟ForegroundColorSpan,hashCode:xxxx':

package com.olfybsppa.inglesaventurero.utils;

import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class DummyTest {
  @Test
  public void testCaptor() {
    SpannableString helper = mock(SpannableString.class);
    ForegroundColorSpan color = mock(ForegroundColorSpan.class);
    helper.setSpan(color, 1, 2, 17);
    ArgumentCaptor<ForegroundColorSpan> captor = ArgumentCaptor.forClass(ForegroundColorSpan.class);
    verify(helper).setSpan(captor.capture(), anyInt(), anyInt(), anyInt());
  }
Run Code Online (Sandbox Code Playgroud)

}

Gradle设置:

这是我的gradle设置:

从整体build.gradle文件

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:1.3.0'
  }
}

allprojects {
  repositories {
    jcenter()
  }
}
Run Code Online (Sandbox Code Playgroud)

从app build.gradle文件:

apply plugin: 'com.android.application'

android {
  compileSdkVersion 20
  buildToolsVersion "20.0.0"

  defaultConfig {
    applicationId "com.olfybsppa.inglesadventurero"
    minSdkVersion 15
    targetSdkVersion 15
    versionCode 5
    versionName "5.0"
  }
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }
  sourceSets {
    main {
    java.srcDirs = ['src/main/java', 'src/main/java/start', 'src/main/java/adapters', 'src/main/java/pagers', 'src/main/java/com.olfybsppa.inglesadventurero/pagers']
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_7
    targetCompatibility JavaVersion.VERSION_1_7
  }
  testOptions {
    unitTests.returnDefaultValues = true
  }
}

dependencies {
  compile 'com.android.support:support-v4:20.0.+'
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.google.android.gms:play-services-ads:6.+'
  testCompile 'junit:junit:4.12'
  testCompile 'org.mockito:mockito-core:1.10.19'
}
Run Code Online (Sandbox Code Playgroud)

PowerMockito也不起作用:

我还使用PowerMockito尝试了以下方法:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SpannableString.class, ForegroundColorSpan.class })
public class DummyTest {
  @Test
  public void testCaptor() {
    SpannableString helper = PowerMockito.mock(SpannableString.class);
    ForegroundColorSpan color = PowerMockito.mock(ForegroundColorSpan.class);
Run Code Online (Sandbox Code Playgroud)

继续像以前一样; 这也无法解决问题.

添加非android界面:

此测试使用returnDefaultValues = true,不使用Powermockito,Object而是使用而不是ForegroundColorSpan.测试以查看子类化android方法和实现非android接口是否有效.

public class SpanPainterTest {
  @Test
  public void testCaptor() {
    SpannableStringSubclass helper = mock(SpannableStringSubclass.class);
    Object color = mock(Object.class);
    helper.setSpan(color, 1, 2, 17);
    ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
    verify(helper).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
    //verify(helper).setSpan(captor.capture(), anyInt(), anyInt(), anyInt());
  }
}
Run Code Online (Sandbox Code Playgroud)

接口:

public interface SpannableStringSuper {
  public void setSpan(Object what, int start, int end, int flags);
}
Run Code Online (Sandbox Code Playgroud)

子类:

public class SpannableStringSubclass extends SpannableString implements SpannableStringSuper {
  public SpannableStringSubclass () {
    super("xxx");
  }
}
Run Code Online (Sandbox Code Playgroud)

这导致与第一次测试非常相似的结果.'any'vs'Mock for Object'.并且'捕获论证'与'模拟对象'.

dur*_*597 5

我无法重现这一点。这是我用来尝试匹配您的场景的代码:

public class DummyTest {
  @Test
  public void testCaptor() {
    SpannableString helper = mock(SpannableString.class);
    ForegroundColorSpan color = mock(ForegroundColorSpan.class);
    helper.setSpan(color, 1, 2, 17);
    ArgumentCaptor<ForegroundColorSpan> captor = ArgumentCaptor.forClass(ForegroundColorSpan.class);
    verify(helper).setSpan(captor.capture(), anyInt(), anyInt(), anyInt());
  }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说很好。我正在使用:

  • 马文
  • 安卓 4.1.1.4
  • JUnit 4.11
  • Mockito 1.10.19

显然,我的工作和你的工作并不意味着课堂之外的东西有问题;它可能是依赖性问题或模拟设置配置问题。

您是否可能以某种方式使用两个不同版本的ForegroundColorSpan?您是否在向我展示过的测试之外设置了 Mockito 设置?

你可以尝试将交换他们两个分开,看是否他们的工作。例如,试试这个:

public class DummyTest {
  @Test
  public void testCaptor() {
    TestSpannable helper = mock(TestSpannable.class);
    ForegroundColorSpan color = mock(ForegroundColorSpan.class);
    helper.setSpan(color, 1, 2, 17);
    ArgumentCaptor<ForegroundColorSpan> captor = ArgumentCaptor.forClass(ForegroundColorSpan.class);
    verify(helper).setSpan(captor.capture(), anyInt(), anyInt(), anyInt());
  }

  public static interface TestSpannable {
    public void setString(Object what, int start, int end, int flags);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,尝试使用SpannableString类型和Object您当前使用的不同的类型进行类似的操作ForegroundColorSpan


我尝试DummyTest再次运行,RETURNS_DEFAULTS根据您的回答- 但在测试类中而不是在构建设置中进行,如下所示:

public class DummyTest {
  @Test
  public void testCaptor() {
    SpannableString helper = mock(SpannableString.class, RETURNS_DEFAULTS);
    ForegroundColorSpan color = mock(ForegroundColorSpan.class, RETURNS_DEFAULTS);
    helper.setSpan(color, 1, 2, 17);
    ArgumentCaptor<ForegroundColorSpan> captor = ArgumentCaptor.forClass(ForegroundColorSpan.class);
    verify(helper).setSpan(captor.capture(), anyInt(), anyInt(), anyInt());
  }
}
Run Code Online (Sandbox Code Playgroud)

有效并且不会抛出您的错误。因此,它与 gradle 构建设置本身有关,而不是RETURNS_DEFAULTS它的表面。