在使用Robolectric测试Android应用程序时,如何创建一个shadow类来解决与自定义属性相关的崩溃?

在使用Robolectric测试Android应用程序时,如何创建一个shadow类来解决与自定义属性相关的崩溃?,android,unit-testing,robolectric,Android,Unit Testing,Robolectric,我正在整合到我的项目中。它使用自定义属性,正在破坏Robolectric。据我所知。注意:构造函数是“敌对的”,因为它在构造时抛出异常,但我现在已经把它们注释掉了 崩溃日志 java.lang.RuntimeException:膨胀布局/main时出错 在com.xtremelabs.roblectric.res.ViewLoader.inflateView(ViewLoader.java:106)上 在com.xtremelabs.roblectric.res.ViewLoader.infla

我正在整合到我的项目中。它使用自定义属性,正在破坏Robolectric。据我所知。注意:构造函数是“敌对的”,因为它在构造时抛出异常,但我现在已经把它们注释掉了

崩溃日志

java.lang.RuntimeException:膨胀布局/main时出错 在com.xtremelabs.roblectric.res.ViewLoader.inflateView(ViewLoader.java:106)上 在com.xtremelabs.roblectric.res.ViewLoader.inflateView(ViewLoader.java:82)上 在com.xtremelabs.roblectric.res.ViewLoader.inflateView(ViewLoader.java:86)上 位于com.xtremelabs.roblectric.res.ResourceLoader.inflateView(ResourceLoader.java:377) 在com.xtremelabs.roblectric.shadows.ShadowLayoutInflater.inflate上(ShadowLayoutInflater.java:43) 在com.xtremelabs.roblectric.shadows.ShadowLayoutInflater.inflate上(ShadowLayoutInflater.java:48) 在android.view.LayoutInflater.inflate(LayoutInflater.java)中 位于com.xtremelabs.roblectric.shadows.ShadowActivity.setContentView(ShadowActivity.java:101) 位于android.app.Activity.setContentView(Activity.java) 位于com.blah.MainActivity.onCreate(MainActivity.java:17) 位于com.blah.MainActivityTest.setUp(MainActivityTest.java:29) 位于org.junit.runners.model.FrameworkMethod$1.runReflectVeCall(FrameworkMethod.java:44) 位于org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) 位于org.junit.runners.model.FrameworkMethod.invokeeexplosive(FrameworkMethod.java:41) 位于org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)

原因:java.lang.ClassCastException:com.blah.support.shadows.ShadowMultiDirectionSlidingDrawer无法强制转换为com.xtremelabs.roblectric.shadows.ShadowView 位于com.xtremelabs.roblectric.roblectric.shadowOf(roblectric.java:857) 位于com.xtremelabs.roblectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:70) 在android.view.ViewGroup.addView(ViewGroup.java)中 位于com.xtremelabs.roblectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:60) 在android.view.ViewGroup.addView(ViewGroup.java)中 位于com.xtremelabs.roblectric.res.ViewLoader$ViewNode.addToParent(ViewLoader.java:217) 位于com.xtremelabs.roblectric.res.ViewLoader$ViewNode.create(ViewLoader.java:180) 在com.xtremelabs.roblectric.res.ViewLoader$ViewNode.inflate(ViewLoader.java:150) 在com.xtremelabs.roblectric.res.ViewLoader$ViewNode.inflate上(ViewLoader.java:153) 在com.xtremelabs.roblectric.res.ViewLoader.inflateView(ViewLoader.java:102)上 ... 还有29个

我正在努力解决这个问题,因为我不太关心这个小部件的测试。基本上,我希望我的测试不会崩溃,并验证视图元素是否出现在屏幕上

有人建议,但我不确定这是否适用于我的情况。正如这个答案所建议的,定制小部件位于与我的com.blah结构并行的包“android”中

我创建了一个小部件的阴影来回避敌意问题(但目前我只是注释掉异常抛出)。最初我想绕过在构造函数中完成的工作,因为它依赖于Robolectric没有正确报告的属性。正在调用影子构造函数,但它会在正常构造函数执行过程中继续。有没有办法绕过额外的建设

ShadowClass

@Implements (MultiDirectionSlidingDrawer.class)
public class ShadowMultiDirectionSlidingDrawer
{
    public void __constructor__( Context context, AttributeSet attrs )
    {
    }

    public void __constructor__( Context context, AttributeSet attrs, int defStyle )
    {
    }
}
自定义测试运行程序

public class CustomTestRunner extends RobolectricTestRunner 
{
    public CustomTestRunner( Class<?> testClass ) throws InitializationError
    {
        super( testClass );
        addClassOrPackageToInstrument("android");
    }

    @Override
    protected void bindShadowClasses()
    {
        super.bindShadowClasses();
        Robolectric.bindShadowClass( ShadowMultiDirectionSlidingDrawer.class );
    }
}
公共类CustomTestRunner扩展了RobolectrictTestRunner
{
公共CustomTestRunner(类testClass)引发初始化错误
{
超级(测试类);
将类或包添加到仪器(“android”);
}
@凌驾
受保护的void bindShadowClasses()
{
super.bindShadowClasses();
Robolectric.bindShadowClass(ShadowMultiDirectionSlidingDrawer.class);
}
}

在测试框架支持之前,这是解决崩溃的正确方法吗?我的影子是做错了什么,还是遗漏了什么?

所以我用一种简单的方法解决了这个问题。我没有将视图作为main.xml的一部分进行膨胀,而是将其放在一个单独的布局文件中。在活动的构造函数中,我在受保护的函数中扩展了布局。在我的测试类中,我扩展了被测试的类,并没有膨胀视图元素,而是使用一个布尔值来跟踪调用的函数

MainActivity.java

public class MainActivity extends FragmentActivity
{
    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        addOptionsShade();
    }

    protected void addOptionsShade()
    {
        ViewGroup viewGroup = (ViewGroup) findViewById( R.id.main_view );
        View view = getLayoutInflater().inflate( R.layout.options_shade, null );
        viewGroup.addView( view );
    }
}
@Test
public void shouldHaveOptionsShade() throws Exception
{
    assertTrue( mainActivity.hostileLibraryWasCalled );
}

class TestMainActivity extends MainActivity
{
    boolean hostileLibraryWasCalled = false;

    @Override
    protected void addOptionsShade()
    {
        hostileLibraryWasCalled = true;
    }
}
MainActivityTest.java

public class MainActivity extends FragmentActivity
{
    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        addOptionsShade();
    }

    protected void addOptionsShade()
    {
        ViewGroup viewGroup = (ViewGroup) findViewById( R.id.main_view );
        View view = getLayoutInflater().inflate( R.layout.options_shade, null );
        viewGroup.addView( view );
    }
}
@Test
public void shouldHaveOptionsShade() throws Exception
{
    assertTrue( mainActivity.hostileLibraryWasCalled );
}

class TestMainActivity extends MainActivity
{
    boolean hostileLibraryWasCalled = false;

    @Override
    protected void addOptionsShade()
    {
        hostileLibraryWasCalled = true;
    }
}

我删除了影子类和绑定,并将库放回一个合理的位置(不在android软件包中)。

这不是作弊吗?…制作一个只为测试而受保护的方法。作弊。。。工程。。。有什么区别吗?:)您可以使用support中的@VisibleForTesting注释:谢谢@Snicolas,我来看看。这不应该是公认的解决方案,因为该解决方案显著改变了生产代码的执行。