Shadow Objects是Robolectric在运行时插入到Android.jar包相应的类中的,它们会实际处理方法的调用,并记录相应的状态,以备在assert的时候进行查询。。请参考官方说明:http://robolectric.org/extending/
产品代码中经常会访问sqlite或者通过网络请求数据。为了保证单元测试的稳定性和运行速度,我们需要隔离这些依赖,那么我们可以使用自定义Shadow。
假如我们的被测代码有如下逻辑:
if (LoginUtil.isOnline()) {
// doSomething...
} else {
// doSomething else...
}
如果LoginUtil.isOnline()
的实现很复杂,很难控制。我们就需要写一个假的实现来替换真实的实现。以方便我们测试上面的逻辑,这个假的实现就叫Shadow。
创建一个ShadowLoginUtil.java
:
@Implements(LoginUtil.class)
public class ShadowLoginUtil {
private static boolean isOnline = false;
@Implementation
public static boolean isOnline() {
return ShadowLoginUtil.isOnline;
}
public static boolean setOnline(boolean isOnline) {
ShadowLoginUtil.isOnline = isOnline;
}
}
有了这个假的实现,我们就可以在测试代码里通过调用ShadowLoginUtil.setOnline(true)
来模拟登录成功的状态。
@RunWith(RobolectricTestRunner.class)
public class CustomShadowExample {
@Test
public void testShadow() throws Exception {
ShadowLoginUtil.setOnline(true);
assertTrue(LoginUtil.isOnline());
ShadowLoginUtil.setOnline(false);
assertFalse(LoginUtil.isOnline());
}
}
但如果现在运行测试,是会失败的。因为Robolectric并不知道ShadowLoginUtil
的存在,想要起作用,我们还要使用自定义TestRunner。
创建MyTestRunner.java
:
import java.util.Arrays;
import java.util.List;
import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.bytecode.ClassInfo;
import org.robolectric.bytecode.Setup;
import org.robolectric.bytecode.ShadowMap;
public class MyTestRunner extends RobolectricTestRunner {
public MyTestRunner(Class<?> arg0) throws InitializationError {
super(arg0);
}
/**
* List of fully qualified class names backed by custom shadows in the test harness.
*/
private static final List<String> CUSTOM_SHADOW_TARGETS = Arrays.asList(LoginUtil.class.getName());// 需要被Shadow的类
@Override
protected ShadowMap createShadowMap() {
return super.createShadowMap()
.newBuilder()
.addShadowClass(ShadowLoginUtil.class) // Shadow类
.build();
}
@Override
public Setup createSetup() {
return new CustomSetup();
}
/**
* Modified Robolectric {@link Setup} that instruments third party classes with custom shadows.
*/
public class CustomSetup extends Setup {
@Override
public boolean shouldInstrument(ClassInfo classInfo) {
return CUSTOM_SHADOW_TARGETS.contains(classInfo.getName())
|| super.shouldInstrument(classInfo);
}
}
}
在上面的代码中,我们主要是告诉Robolectric我们想用ShadowLoginUtil
来替换LoginUtil
。
修改测试代码,使用自定义的TestRunner:
@RunWith(MyTestRunner.class)
public class CustomShadowExample {
......
}
再次运行测试,就可以顺利通过了。