Robolectric影子功能失效

13

我正在尝试使用Robolectric创建一个测试。我的目标是能够从自定义行为中替换一个类的功能(例如来自库且无法修改代码的类)。

我创建了这个小测试来模拟我想要做的事情:

@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowMessenger.class})
public class TestShadow {

    @Test
    public void testMessenger() {
        OriginalMessenger messenger = new OriginalMessenger();
        String message = messenger.getMessage();
        Assert.assertEquals("Shadow messenger", message);
    }

    public static class OriginalMessenger {

        public String getMessage() {
            return "Original messenger";
        }
    }

    @Implements(OriginalMessenger.class)
    public static class ShadowMessenger extends OriginalMessenger {

        @Implementation
        public String getMessage() {
            return "Shadow messenger";
        }
    }
}
在这个例子中,OriginalMessenger是库中提供默认功能的类。而ShadowMessenger是包含我想要应用于每次使用OriginalMessenger时的自定义行为的类。
但是当我运行测试时,它失败了。 message的内容是"Original messenger",好像ShadowMessenger从未被使用过。
我做错了什么?

2
为什么要创建一个影子?为什么不使用“模拟”或“存根”来替代它? - Jared Burrows
我如何使用模拟和存根来实现相同的结果? - Mauricio Togneri
是的,我有这些:testCompile 'org.powermock:powermock-module-junit4:1.6.2' testCompile 'org.powermock:powermock-api-easymock:1.6.2' testCompile 'org.powermock:powermock-core:1.6.2' testCompile 'org.easymock:easymock:3.3.1'这是代码: https://github.com/mauriciotogneri/android-studio-template/tree/master/app/src/test/java/com/example - Mauricio Togneri
有很多种方法可以做到这一点。如果您的类是一个简单的POJO,则扩展它并覆盖一个方法对于测试而言会是一个“存根”或“虚拟”类非常好用。 如果您的类需要互联网或数据库,那么最好采用“模拟”或“间谍”方式,并使其“返回”您预期的输出。 - Jared Burrows
我不知道这是否适用于我的情况。我有一个类 A 需要测试,它需要类 B 才能正常工作。但是 B 在库中,所以我不能修改或扩展它。我想测试 A,但是不想使用原始的 B,而是想使用一个虚拟的 B,同时又不改变 A 的代码。这就是我认为可以使用 shadows 的原因。我的想法是:每当代码需要 B 时,就使用虚拟的 B 替代它。其中一种可能的解决方案是使用依赖注入,在其中将 B(或虚拟的 B)注入到 A 中进行使用。但我想知道是否可以使用 shadows 实现此功能。 - Mauricio Togneri
显示剩余6条评论
1个回答

16

原本你只能影子Android类。但是使用自定义Robolectric测试运行器,你也可以影子自己的类。

Robolectric 3.1.4版本中(完全删除了RobolectricGradleTestRunner,所以你需要在RobolectricTestRunner中覆盖下面描述的方法)。

@Override
protected ShadowMap createShadowMap() {
    return new ShadowMap.Builder()
        .addShadowClass(OriginalMessenger.class, ShadowMessenger.class, true, true, true)
        .build();
}

Robolectric 3.0

@Override
public InstrumentationConfiguration createClassLoaderConfig() {
    InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
    builder.addInstrumentedClass(OriginalMessenger.class.getName());
    return builder.build();
}

Robolectric 2.4

@Override
protected ClassLoader createRobolectricClassLoader(Setup setup, SdkConfig sdkConfig) {
    return super.createRobolectricClassLoader(new ExtraShadows(setup), sdkConfig);
}

class ExtraShadows extends Setup {
    private Setup setup;

    public ExtraShadows(Setup setup) {
        this.setup = setup;
    }

    public boolean shouldInstrument(ClassInfo classInfo) {
        boolean shoudInstrument = setup.shouldInstrument(classInfo);
        return shoudInstrument
                || classInfo.getName().equals(OriginalMessenger.class.getName());
    }
}

示例项目 https://github.com/nenick/android-gradle-template/


这是堆栈跟踪: http://pastebin.com/5pVB7zrY这些是我正在使用的类: https://github.com/mauriciotogneri/android-studio-template/tree/master/app/src/test/java/com/example - Mauricio Togneri
我去掉了继承,现在它像魔法一样正常工作。谢谢! - Mauricio Togneri
1
嗨@nenick,关于robolectric 3.0怎么样?我在这个版本中可以找到bytecode.Setup类。 - Phien
是的,它已被替换。一个可能的新位置来添加自己的阴影可以是 protected ShadowMap createShadowMap() {...} - nenick
@Phien:我在 Robolectric 3.0 中通过重写测试运行器上的 createClassLoaderConfig() 方法并构建一个包含我的插桩包和类的 InstrumentationConfiguration 实例来完成了它。 - corsair992
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接