Android单元测试(一)

本文介绍了Android单元测试入门所需了解的内容,包括JUnit、Mockito和PowerMock的使用,怎样写MVP中的P层的单元测试以及RxJava相关测试。

本文首发:http://yuweiguocn.github.io/
新浪微博:@于卫国

《望岳》
岱宗夫如何?齐鲁青未了。
造化钟神秀,阴阳割昏晓。
荡胸生层云,决眦入归鸟。
会当凌绝顶,一览众山小。
-唐,杜甫

前言

最近刚学习了下Android单元测试,看了小创作的中文系列教程,也看了官方的英文教程,刚接触那几天那叫一个痛苦,简直就是陷入了泥潭,接触到了好多框架,并且不清楚什么情况下到底用哪个框架。我们先从最基础的做起,项目目前使用的是MVP架构,先从P层开始做单元测试,由浅入深。之前老说MVP方便单元测试,到底哪方便了,等真开始做单元测试的时候,才真正开始理解MVP架构。首先是我们的P层是不引入任何Android框架代码的(如果引用了Android框架的代码需要引入其它框架才能做测试),之前理解不够,在P层执行Toast(应该在View层执行)操作了。

我们先来看下需要了解的框架:

  • JUnit:Java 编程语言的单元测试框架,主要用于断言。
  • Mockito:Java界使用最广泛的一个mock框架,mock表示在测试环境中创建一个类的虚假对象方便用于验证。Mockito不支持mock匿名类、final类、静态方法和private方法。
  • PowerMock:扩展了Mockito,支持Mock静态、final、私有方法等。

JUnit

仓库地址:https://github.com/junit-team/junit4

添加依赖:

testCompile 'junit:junit:4.12'

Assert类中比较重要的方法如下:

序号 方法和描述
1 void assertEquals(boolean expected, boolean actual) 检查两个变量或者等式是否平衡
2 void assertFalse(boolean condition) 检查条件是假的
3 void assertNotNull(Object object) 检查对象不是空的
4 void assertNull(Object object) 检查对象是空的
5 void assertTrue(boolean condition) 检查条件为真
6 void fail() 在没有报告的情况下使测试不通过

注意:上面的每一个方法,都有一个重载的方法,可以在前面加一个String类型的参数,表示如果验证失败的话,将用这个字符串作为失败的结果报告。

JUnit 中的注解及含义:

序号 注解和描述
1 @Test 这个注释说明依附在 JUnit 的 public void 方法可以作为一个测试案例。
2 @Before 有些测试在运行前需要创造几个相似的对象。在 public void 方法加该注释是因为该方法需要在 test 方法前运行。
3 @After 如果你将外部资源在 Before 方法中分配,那么你需要在测试运行后释放他们。在 public void 方法加该注释是因为该方法需要在 test 方法后运行。
4 @BeforeClass 在 public void 方法加该注释是因为该方法需要在类中所有方法前运行。被此注解修饰的方法必须是静态的。
5 @AfterClass 它将会使方法在所有测试结束后执行。这个可以用来进行清理活动。 被此注解修饰的方法必须是静态的。
6 @Ignore 这个注释是用来忽略有关不需要执行的测试的。

超时

Junit 提供了一个指定超时参数。如果一个测试用例执行的毫秒数超过了指定的参数值,那么 Junit 将自动将它标记为失败。

@Test(timeout=1000)
public void testPrintMessage() {
    ...
}

捕获异常

Junit 提供了一个捕获异常的参数。你可以测试代码是否抛出了预期的异常。

参数化

Junit 4 引入了一个新的功能参数化测试。参数化测试允许开发人员使用不同的值反复运行同一个测试。你可以遵循下面的步骤来创建参数化测试。

  • 在测试类上添加注解@RunWith(Parameterized.class)。
  • 创建一个由 @Parameters 注解的公共的静态方法,它返回一个对象的集合(数组)来作为测试数据集合。
  • 创建一个公共的构造函数,参数个数和类型与提供的数据集合一一对应。
  • 为每一列测试数据创建一个实例变量。
  • 用实例变量作为测试数据的来源来创建你的测试用例。
public class PrimeNumberChecker {
   public Boolean validate(final Integer primeNumber) {
      for (int i = 2; i < (primeNumber / 2); i++) {
         if (primeNumber % i == 0) {
            return false;
         }
      }
      return true;
   }
}


import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.Before;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;

@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {
   private Integer inputNumber;
   private Boolean expectedResult;
   private PrimeNumberChecker primeNumberChecker;

   @Before
   public void initialize() {
      primeNumberChecker = new PrimeNumberChecker();
   }

   // Each parameter should be placed as an argument here
   // Every time runner triggers, it will pass the arguments
   // from parameters we defined in primeNumbers() method
   public PrimeNumberCheckerTest(Integer inputNumber,
      Boolean expectedResult) {
      this.inputNumber = inputNumber;
      this.expectedResult = expectedResult;
   }

   @Parameterized.Parameters
   public static Collection primeNumbers() {
      return Arrays.asList(new Object[][] {
         { 2, true },
         { 6, false },
         { 19, true },
         { 22, false },
         { 23, true }
      });
   }

   // This test will run 4 times since we have 5 parameters defined
   @Test
   public void testPrimeNumberChecker() {
      System.out.println("Parameterized Number is : " + inputNumber);
      assertEquals(expectedResult,
      primeNumberChecker.validate(inputNumber));
   }
}

运行结果:

Mockito

所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:

  • 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
  • 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作

注意:Mockito不支持mock匿名类、final类、静态方法和private方法。

添加依赖:

 repositories {
   jcenter()
 }
 dependencies {
   testCompile "org.mockito:mockito-core:+"
 }

验证行为

//静态导入
 import static org.mockito.Mockito.*;

 //创建mock
 List mockedList = mock(List.class);

 //使用mock对象
 mockedList.add("one");
 mockedList.clear();

 //验证
 verify(mockedList).add("one");
 verify(mockedList).clear();

stubbing

//你可以mock一个实体类
LinkedList mockedList = mock(LinkedList.class);

//处理指定行为
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

//将会打印"first"
System.out.println(mockedList.get(0));

//将会抛出runtime exception
System.out.println(mockedList.get(1));

//将会打印"null" 因为get(999)没有指定行为
System.out.println(mockedList.get(999));

//尽管可以验证一个指定行为的调用,但通常这是多余的
//如果你关心get(0)的返回,那会被其它事情打断
//如果你不关心get(0)的返回,那你不应该指定行为,直接验证就可以了
verify(mockedList).get(0);
  • 默认情况下,所有方法都会有返回值,一个 mock 将返回 null,一个原始/基本类型的包装值或适当的空集。例如,对于一个 int/Integer 就是 0,而对于 boolean/Boolean 就是 false。
  • Stubbing 可以被覆盖。
  • 一旦 stub,该方法将始终返回一个 stub 的值,无论它被调用多少次。
  • stubbing 的顺序是重要的。

参数匹配器

Mockito 验证参数值使用 Java 方式:通过使用 equals() 方法。有时,当需要额外的灵活性,可以使用参数匹配器:

//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");

//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid()))).thenReturn("element");

//following prints "element"
System.out.println(mockedList.get(999));

//you can also verify using an argument matcher
verify(mockedList).get(anyInt());

参数匹配器允许灵活的验证或 stubbing。自定义参数的匹配信息,请查看 Javadoc 中 ArgumentMatcher 类。如果你正在使用参数的匹配,所有的参数都由匹配器来提供。
下面的示例演示验证,但同样适用于 stubbing:

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher

verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher.

验证调用次数

//using mock
mockedList.add("once");

mockedList.add("twice");
mockedList.add("twice");

mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");

//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");

//verification using atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");

times(1) 是默认的,因此,使用的 times(1) 可以显示的省略。

Stubbing void 方法处理异常

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();

有序的验证

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);

//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");

//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);

//following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");

// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);

//using mocks
firstMock.add("was called first");
secondMock.add("was called second");

//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);

//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");

// Oh, and A + B can be mixed together at will

有序验证是为了灵活,你不必一个接一个验证所有的交互。
此外,您还可以通过创建 InOrder 对象传递只与有序验证相关的 mock 。

验证 mock 上不会发生交互

//using mocks - only mockOne is interacted
mockOne.add("one");

//ordinary verification
verify(mockOne).add("one");

//verify that method was never called on a mock
verify(mockOne, never()).add("two");

//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);

寻找多余的调用

//using mocks
mockedList.add("one");
mockedList.add("two");

verify(mockedList).add("one");

//following verification will fail
verifyNoMoreInteractions(mockedList);

注意:不建议 verifyNoMoreInteractions() 在每个测试方法中使用。 verifyNoMoreInteractions() 是从交互测试工具包一个方便的断言。只有与它的相关时才使用它。滥用它导致难以维护。

标准创建 mock 方式——使用 @Mock 注解

  • 最小化可重用 mock 创建代码
  • 使测试类更加可读性
  • 使验证错误更加易读,因为字段名称用于唯一识别 mock
public class ArticleManagerTest {
 @Mock
 private ArticleCalculator calculator;
 @Mock
 private ArticleDatabase database;
 @Mock
 private UserProvider userProvider;

 private ArticleManager manager;

 @Before
 public void init(){
    MockitoAnnotations.initMocks(this);
 }

 ...

可以使用内建 runner: MockitoJUnitRunner 或者 rule: MockitoRule
更多详见 MockitoAnnotations
另外也可以通过在类上使用@RunWith(MockitoJUnitRunner.class)来达到相同的效果。

Stubbing 连续调用(迭代器式的 stubbing)

when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");

//First call: throws runtime exception:
mock.someMethod("some arg");

//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));

//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));


下面是一个精简版本:
when(mock.someMethod("some arg"))
.thenReturn("one", "two", "three");

回调 Stubbing

Mockito允许使用泛型 Answer 接口。我们建议您只使用thenReturn() 或 thenThrow() 来 stubbing 。但是,如果你有一个需要 stub 到泛型 Answer 接口,这里有一个例子:

when(mock.someMethod(anyString())).thenAnswer(new Answer() {
   Object answer(InvocationOnMock invocation) {
       Object[] args = invocation.getArguments();
       Object mock = invocation.getMock();
       return "called with arguments: " + args;
   }
});

//the following prints "called with arguments: foo"
System.out.println(mock.someMethod("foo"));

doThrow() 家族方法

Stubbing void 方法,需要不同的 when(Object)方法,因为编译器不喜欢括号内无效的方法。在用于 Stubbing void 方法中,doThrow(Throwable…) 代替了 stubVoid(Object)。主要原因是提高可读性和与 doAnswer() 保持一致性。
当你想用 stub void 方法时,使用 doThrow():

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();

在调用 when() 的相应地方可以使用 doThrow(), doAnswer(), doNothing(), doReturn() 和 doCallRealMethod(),当stub void 方法和stub 方法在 spy 对象时(见下面),可以不止一次的 stub 相同的方法,在测试的中期来改变 mock 的行为。在所有的 stubbing 调用时,你会更加倾向于使用这些方法来代替 when()。

spy

spy可以实现调用对象的默认实现。
如果不指定mock方法的特定行为,一个mock对象的所有非void方法都将返回默认值:int、long类型方法将返回0,boolean方法将返回false,对象方法将返回null等等;而void方法将什么都不做。

区别:spy对象的方法默认调用真实的逻辑,mock对象的方法默认什么都不做,或直接返回默认值。

//假设目标类的实现是这样的
public class PasswordValidator {
    public boolean verifyPassword(String password) {
        return "xiaochuang_is_handsome".equals(password);
    }
}

@Test
public void testSpy() {
    //跟创建mock类似,只不过调用的是spy方法,而不是mock方法。spy的用法
    PasswordValidator spyValidator = Mockito.spy(PasswordValidator.class);

    //在默认情况下,spy对象会调用这个类的真实逻辑,并返回相应的返回值,这可以对照上面的真实逻辑
    spyValidator.verifyPassword("xiaochuang_is_handsome"); //true
    spyValidator.verifyPassword("xiaochuang_is_not_handsome"); //false

    //spy对象的方法也可以指定特定的行为
    Mockito.when(spyValidator.verifyPassword(anyString())).thenReturn(true);

    //同样的,可以验证spy对象的方法调用情况
    spyValidator.verifyPassword("xiaochuang_is_handsome");
    Mockito.verify(spyValidator).verifyPassword("xiaochuang_is_handsome"); //pass
}

捕获参数

ArgumentCaptor类允许我们在verification期间访问方法的参数。得到方法的参数后我们可以使用它进行测试。

@Captor
ArgumentCaptor<List> arguments;

@Before
public void init() {
    // Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
    // inject the mocks in the test the initMocks method needs to be called.
    MockitoAnnotations.initMocks(this);
}

@Test
public void testArgument() {
    List asList = Arrays.asList("someElement_test", "someElement");
    final List mockedList = mock(List.class);
    mockedList.addAll(asList);

    verify(mockedList).addAll(arguments.capture());
    final List capturedArgument = arguments.getValue();
    assertEquals(capturedArgument.get(1),"someElement");
}

PowerMock

仓库地址:https://github.com/powermock/powermock

//添加依赖
testCompile "org.powermock:powermock-module-junit4:1.6.4"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.4"
testCompile "org.powermock:powermock-api-mockito:1.6.4"
testCompile "org.powermock:powermock-classloading-xstream:1.6.4"

PowerMock扩展了EasyMock和Mockito框架,增加了对static和final方法mock支持等功能。
PowerMock有两个重要的注解:

  • @RunWith(PowerMockRunner.class)
  • @PrepareForTest( { YourClassWithEgStaticMethod.class })

如果你的测试用例里没有使用注解@PrepareForTest,那么可以不用加注解@RunWith(PowerMockRunner.class),反之亦然。当你需要使用PowerMock强大功能(Mock静态、final、私有方法等)的时候,就需要加注解@PrepareForTest。

普通Mock: Mock参数传递的对象

public boolean callArgumentInstance(File file) {
     return file.exists();
}

@Test
public void testCallArgumentInstance() {
    File file = PowerMockito.mock(File.class);
    ClassUnderTest underTest = new ClassUnderTest();
    PowerMockito.when(file.exists()).thenReturn(true);
    Assert.assertTrue(underTest.callArgumentInstance(file));
}

说明:普通Mock不需要加@RunWith和@PrepareForTest注解。

Mock方法内部new出来的对象

public class ClassUnderTest {
    public boolean callInternalInstance(String path) {
        File file = new File(path);
        return file.exists();
    }
}

@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
    @Test
    @PrepareForTest(ClassUnderTest.class)
    public void testCallInternalInstance() throws Exception {
        File file = PowerMockito.mock(File.class);
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.whenNew(File.class).withArguments("bbb").thenReturn(file);
        PowerMockito.when(file.exists()).thenReturn(true);
        Assert.assertTrue(underTest.callInternalInstance("bbb"));
    }
}

说明:当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是需要mock的new对象代码所在的类。

Mock普通对象的final方法

public class ClassUnderTest {
    public boolean callFinalMethod(ClassDependency refer) {
        return refer.isAlive();
    }
}
public class ClassDependency {  
    public final boolean isAlive() {
        // do something
        return false;
    }
}
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
    @Test
    @PrepareForTest(ClassDependency.class)
    public void testCallFinalMethod() {
        ClassDependency depencency =  PowerMockito.mock(ClassDependency.class);
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.when(depencency.isAlive()).thenReturn(true);
        Assert.assertTrue(underTest.callFinalMethod(depencency));
    }
}

说明: 当需要mock final方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是final方法所在的类。

Mock普通类的静态方法

public class ClassUnderTest {
    public boolean callStaticMethod() {
        return ClassDependency.isExist();
    }  
}
public class ClassDependency {
    public static boolean isExist() {
        // do something
        return false;
    }
}

@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
    @Test
    @PrepareForTest(ClassDependency.class)
    public void testCallStaticMethod() {
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.mockStatic(ClassDependency.class);
        PowerMockito.when(ClassDependency.isExist()).thenReturn(true);
        Assert.assertTrue(underTest.callStaticMethod());
    }
}

说明:当需要mock静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类。

Mock RxJava

@RunWith(PowerMockRunner.class)
@PrepareForTest(AppApi.class)
public class SubmitPresenterTest {
    @Mock
    private SubmitContract.View mView;
    private SubmitContract.Presenter mPresenter;


    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        mPresenter = new SubmitPresenter(mView);
    }

    @Test
    public void testLoadDataSuccess() {
        mockStatic(AppApi.class);
        //Observable.just(new InsuranceResultBean())会调用onNext方法
        when(AppApi.getSubmitInsuranceResult(any(InsuranceInfoBean.class))).thenReturn(Observable.just(new InsuranceResultBean()));
        //Observable.<InsuranceResultBean>empty() 会直接调用onCompleted()方法
        when(AppApi.getSubmitInsuranceResult(any(InsuranceInfoBean.class))).thenReturn(Observable.<InsuranceResultBean>empty());
        mPresenter.submit(new InsuranceInfoBean());
        verify(mView).onSuccess(any(InsuranceResultBean.class));
    }


    @Test
    public void testLoadDataFail() {
        mockStatic(AppApi.class);
        //Observable.<InsuranceResultBean>error(new BusinessException(-3, "test fail"))会调用onError方法
        when(AppApi.getSubmitInsuranceResult(any(InsuranceInfoBean.class))).thenReturn(Observable.<InsuranceResultBean>error(new BusinessException(-3, "test fail")));
        mPresenter.submit(new InsuranceInfoBean());
        verify(mView).showVerifyDialog();
    }

    @Test
    public void testLoadDataFailToast() {
        mockStatic(AppApi.class);
        when(AppApi.getSubmitInsuranceResult(any(InsuranceInfoBean.class))).thenReturn(Observable.<InsuranceResultBean>error(new BusinessException(-4, "test fail")));
        mPresenter.submit(new InsuranceInfoBean());
        verify(mView).showVerifyDialog();
        verify(mView).onToast(any(String.class));
    }
}

Mock 私有方法

public class ClassUnderTest {
    public boolean callPrivateMethod() {
        return isExist();
    }       
    private boolean isExist() {
        return false;
    }
}

@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
    @Test
    @PrepareForTest(ClassUnderTest.class)
    public void testCallPrivateMethod() throws Exception {
       ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class);
       PowerMockito.when(underTest.callPrivateMethod()).thenCallRealMethod();
       PowerMockito.when(underTest, "isExist").thenReturn(true);
       Assert.assertTrue(underTest.callPrivateMethod());
    }
}

说明:和Mock普通方法一样,只是需要加注解@PrepareForTest(ClassUnderTest.class),注解里写的类是私有方法所在的类。

Mock系统类的静态和final方法

public class ClassUnderTest {
    public boolean callSystemFinalMethod(String str) {
        return str.isEmpty();
    }
    public String callSystemStaticMethod(String str) {
        return System.getProperty(str);
    }
}

@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
  @Test
  @PrepareForTest(ClassUnderTest.class)
  public void testCallSystemStaticMethod() {
      ClassUnderTest underTest = new ClassUnderTest();
      PowerMockito.mockStatic(System.class);
      PowerMockito.when(System.getProperty("aaa")).thenReturn("bbb");
      Assert.assertEquals("bbb", underTest.callJDKStaticMethod("aaa"));
  }
}

说明:和Mock普通对象的静态方法、final方法一样,只不过注解@PrepareForTest里写的类不一样 ,注解里写的类是需要调用系统方法所在的类。

PowerMock 简单实现原理

  • 当某个测试方法被注解@PrepareForTest标注以后,在运行测试用例时,会创建一个新的org.powermock.core.classloader.MockClassLoader实例,然后加载该测试用例使用到的类(系统类除外)。
  • PowerMock会根据你的mock要求,去修改写在注解@PrepareForTest里的class文件(当前测试类会自动加入注解中),以满足特殊的mock需求。例如:去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等。
  • 如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。

测试RxJava

TestSubscriber用于单元测试,你可以用来执行断言,检查接收的事件,或者包装一个被mock的Subscriber。可以通过RxJava提供的Hook的方式修改线程为立即执行。

public Observable<String> getTestRxJava() {
    return Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            subscriber.onNext("test");
            subscriber.onCompleted();
        }
    }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}



@Test
public void testSync() {
    //通过Hook的方式修改主线程为immediate
    RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
        @Override
        public Scheduler getMainThreadScheduler() {
            return Schedulers.immediate();
        }
    });
    //通过Hook的方式修改io线程为immediate
    RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
        @Override
        public Scheduler getIOScheduler() {
            return Schedulers.immediate();
        }
    });

    TestSubscriber<String> testSubscriber = new TestSubscriber<>();
    mPresenter.getTestRxJava().subscribe(testSubscriber);
    testSubscriber.assertValue("test");
}

图为TestSubscriber类中提供的方法。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 在博客Android单元测试之Mockito中,主要介绍了Mockito测试框架和基本使用。在博客结束时,我们提出...
    水木飞雪阅读 13,606评论 5 18
  • 在博客Android单元测试之JUnit4中,我们简单地介绍了:什么是单元测试,为什么要用单元测试,并展示了一个简...
    水木飞雪阅读 9,391评论 4 18
  • 单元测试 单测定义 单元测试(Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进...
    运维开发笔记阅读 1,979评论 0 2
  • 01 从村北回家时,大意的我被一个来路不明的男人手中的大铁钳套住脖子挣脱不开时,我顿时觉得天都黑了。 村南的大块头...
    也许我在等时光阅读 890评论 4 5