Robolectricを使用したカスタムビューのテスト


82

Robolectric 2.1.1で単体テストを実行しようとしていますが、カスタムレイアウト(ViewPagerIndicatorクラスなど)を拡張することができません。これが私のレイアウトだとしましょう:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="test"
            android:id="@+id/test_test"/>

    <com.viewpagerindicator.CirclePageIndicator
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>

</LinearLayout>

これを私のテストクラスと考えてください。

@RunWith(RobolectricTestRunner.class)
public class TestRoboActivityTest {
    private TestRoboActivity mActivity;

    @Before
    public void setUp() throws Exception {
        mActivity = Robolectric.buildActivity(TestRoboActivity.class).create().get();
    }

    @After
    public void tearDown() throws Exception {
        mActivity = null;
    }

    @Test
    public void testSanity() throws Exception {
        Assert.assertNotNull(mActivity);
    }
}

'mvn clean test'を実行すると、

エラーのあるテスト:
testSanity(TestRoboActivityTest):XMLファイル。\ res \ layout \ test.xml行#-1(申し訳ありませんが、まだ実装されていません):クラスcom.viewpagerindicator.CirclePageIndicatorの拡張中にエラーが発生しました

かっこいいので、カスタムビューはまだサポートされていないようです。彼らのウェブサイトでサンプルのRobolectricプロジェクトを確認すると、1つの解決策はLayoutInflaterからレイアウトを膨らませることです。

@RunWith(RobolectricTestRunner.class)
public class TestRoboActivityTest {
    private View mTestRoboActivityView;

    @Before
    public void setUp() throws Exception {
        mTestRoboActivityView = LayoutInflater.from(new Activity()).inflate(R.layout.test, null);
    }

    @After
    public void tearDown() throws Exception {
        mTestRoboActivityView = null;
    }

    @Test
    public void testSanity() throws Exception {
        Assert.assertNotNull(mTestRoboActivityView);
    }
}

その結果:

エラーのあるテスト: 
testSanity(TestRoboActivityTest):XMLファイル。\ res \ layout \ test.xml行#-1(申し訳ありませんが、まだ実装されていません):クラスcom.viewpagerindicator.CirclePageIndicatorの拡張中にエラーが発生しました

私の最後の手段は、シャドウクラスを使おうとしたことでした。

@Implements(CirclePageIndicator.class)
public class CirclePageIndicatorShadow implements PageIndicator {

    @Override
    @Implementation
    public void setViewPager(ViewPager view) {
        // Stub
    }

    // etc.
}

とを使用し@Config(shadows = {CirclePageIndicatorShadow.class})ます。これもまた結果として

エラーのあるテスト: 
testSanity(TestRoboActivityTest):XMLファイル。\ res \ layout \ test.xml行#-1(申し訳ありませんが、まだ実装されていません):クラスcom.viewpagerindicator.CirclePageIndicatorの拡張中にエラーが発生しました

編集(2014年12月)

次のstracktraceは、後でDavidRabinowitzによって追加されたことに注意してください。関連している間、それは私がその時に直面していた問題ではありません。


スタックトレースは次のとおりです。

android.view.InflateException: XML file .\res\layout\activity_home.xml line #-1 (sorry, not yet implemented): Error inflating class com.test.custom.RobotoTextView
    at android.view.LayoutInflater.createView(LayoutInflater.java:613)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
    at org.robolectric.tester.android.view.RoboWindow.setContentView(RoboWindow.java:82)
    at org.robolectric.shadows.ShadowActivity.setContentView(ShadowActivity.java:273)
    at android.app.Activity.setContentView(Activity.java)
    at com.example.testrobocustomfont.MainActivity.onCreate(MainActivity.java:12)
    at com.example.testrobocustomfont.MainActivityTest.setUp(MainActivityTest.java:28)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:241)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_createView(LayoutInflater.java:587)
    at android.view.LayoutInflater.createView(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_createViewFromTag(LayoutInflater.java:687)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:396)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:352)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at org.robolectric.tester.android.view.RoboWindow.setContentView(RoboWindow.java:82)
    at org.robolectric.shadows.ShadowActivity.setContentView(ShadowActivity.java:273)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.robolectric.bytecode.ShadowWrangler$ShadowMethodPlan.run(ShadowWrangler.java:455)
    at android.app.Activity.setContentView(Activity.java)
    at com.example.testrobocustomfont.MainActivity.onCreate(MainActivity.java:12)
    at com.example.testrobocustomfont.MainActivityTest.setUp(MainActivityTest.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    ... 22 more
Caused by: java.lang.RuntimeException: error converting RobotoMedium.ttf using EnumConverter
    at org.robolectric.shadows.Converter.convertAndFill(Converter.java:150)
    at org.robolectric.shadows.Converter.convertAndFill(Converter.java:50)
    at org.robolectric.shadows.ShadowResources.createTypedArray(ShadowResources.java:228)
    at org.robolectric.shadows.ShadowResources.attrsToTypedArray(ShadowResources.java:203)
    at org.robolectric.shadows.ShadowResources.access$000(ShadowResources.java:51)
    at org.robolectric.shadows.ShadowResources$ShadowTheme.obtainStyledAttributes(ShadowResources.java:460)
    at android.content.res.Resources$Theme.obtainStyledAttributes(Resources.java)
    at android.widget.TextView.__constructor__(TextView.java:561)
    at android.widget.TextView.<init>(TextView.java:447)
    at android.widget.TextView.<init>(TextView.java:442)
    at com.test.custom.RobotoTextView.<init>(RobotoTextView.java:16)
    at android.view.LayoutInflater.createView(LayoutInflater.java:587)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
    at org.robolectric.tester.android.view.RoboWindow.setContentView(RoboWindow.java:82)
    at org.robolectric.shadows.ShadowActivity.setContentView(ShadowActivity.java:273)
    at android.app.Activity.setContentView(Activity.java)
    at com.example.testrobocustomfont.MainActivity.onCreate(MainActivity.java:12)
    at com.example.testrobocustomfont.MainActivityTest.setUp(MainActivityTest.java:28)
    ... 22 more
Caused by: java.lang.RuntimeException: no value found for RobotoMedium.ttf
    at org.robolectric.shadows.Converter$EnumOrFlagConverter.findValueFor(Converter.java:375)
    at org.robolectric.shadows.Converter$EnumConverter.fillTypedValue(Converter.java:343)
    at org.robolectric.shadows.Converter$EnumConverter.fillTypedValue(Converter.java:336)
    at org.robolectric.shadows.Converter.convertAndFill(Converter.java:148)
    at org.robolectric.shadows.Converter.convertAndFill(Converter.java:50)
    at org.robolectric.shadows.ShadowResources.createTypedArray(ShadowResources.java:228)
    at org.robolectric.shadows.ShadowResources.attrsToTypedArray(ShadowResources.java:203)
    at org.robolectric.shadows.ShadowResources.access$000(ShadowResources.java:51)
    at org.robolectric.shadows.ShadowResources$ShadowTheme.obtainStyledAttributes(ShadowResources.java:460)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.robolectric.bytecode.ShadowWrangler$ShadowMethodPlan.run(ShadowWrangler.java:455)
    at android.content.res.Resources$Theme.obtainStyledAttributes(Resources.java)
    at android.widget.TextView.$$robo$$TextView_347d___constructor__(TextView.java:561)
    at android.widget.TextView.<init>(TextView.java:447)
    at android.widget.TextView.<init>(TextView.java:442)
    at com.test.custom.RobotoTextView.<init>(RobotoTextView.java:16)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_createView(LayoutInflater.java:587)
    at android.view.LayoutInflater.createView(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_createViewFromTag(LayoutInflater.java:687)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:396)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at android.view.LayoutInflater.$$robo$$LayoutInflater_1d1f_inflate(LayoutInflater.java:352)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at org.robolectric.tester.android.view.RoboWindow.setContentView(RoboWindow.java:82)
    at org.robolectric.shadows.ShadowActivity.setContentView(ShadowActivity.java:273)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.robolectric.bytecode.ShadowWrangler$ShadowMethodPlan.run(ShadowWrangler.java:455)
    at android.app.Activity.setContentView(Activity.java)
    at com.example.testrobocustomfont.MainActivity.onCreate(MainActivity.java:12)
    at com.example.testrobocustomfont.MainActivityTest.setUp(MainActivityTest.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    ... 22 more

正しい方向に向けてください。私は考えが足りません。ありがとう。


1
フルスタックトレースを投稿できますか?
Corey D

カスタムフォントを使用していますか、それともカスタムフォントを使用するカスタムビューを使用していますか?@joecksは彼らの答えで正しい方向に進んでいると思います。Androidはプレビューでカスタムフォントをレンダリングできません(つまり、Eclipseでxmlを表示している場合)。同じ問題がここで発生している可能性があります。テキストビューを制御する場合は、スタイルを取得するコードをif (!isInEditMode())
karl

レイアウトxmlのグラフィカルプレビューでカスタムビューを表示できますか?
JstnPwll 2014年


1
OPは、私たちが支援できるように、スタックトレースを投稿できますか?他の誰かのスタックトレースを持つことはあまり役に立ちません。ありがとう。
カレブ2015年

回答:


4

ビューを使用するアクティビティを使用して、同じテストクラスでビューをテストします。この場合、私はRobolectricにそのアクティビティのインスタンスを与えるように指示し、そこから膨張したビューのインスタンスを取得します。

@Before
public void setup(){
    activity = Robolectric.buildActivity(MyActivity.class).create().get();
    View view = LayoutInflater.from(activity).inflate(R.layout.myView, null);
}
@Test
 public void allElementsInViewProduct(){
     assertNotNull(view.findViewById(R.id.view1));
     assertNotNull(view.findViewById(R.id.view2));
     assertNotNull(view.findViewById(R.id.view3));
 }

LE:私はRobolectric 3.0を使用しているので、これがあなたに当てはまるかどうかはわかりません。


3

問題:

この問題が発生するのは、gradleがプロジェクトの依存関係(例:)compile project(':lib-custom')と外部の依存関係(例:)compile 'lib.package:name:1.1.0'を異なる方法でマージするためです。依存関係がマージされた後、アプリにはR.javaすべてのリソースフィールド(色、ID、ドローアブルなど)を含むファイルがあります。ただし、R.javaサブモジュールと外部依存関係をマージした後、生成されたファイルは異なって見えます。

この問題は、サブモジュールにカスタムビューがあるプロジェクトでのみ発生します。外部依存関係の場合、簡単に修正できる別の問題があります。依存関係の種類については、こちらをご覧ください

プロジェクトの依存関係の場合、結果R.javaファイルにはすべてのリソース識別子が含まれますが、サブモジュールの識別子は元の整数識別子と等しくありません。

com.lib.custom.R.color.primary != com.main.project.R.color.primary

外部依存関係のマージR.javaファイルの場合、すべての外部依存関係からのR.javaファイルのマージ結果のみ

com.lib.custom.R.color.primary == com.main.project.R.color.primary

解決:

私は2つの可能な解決策を見つけました:

  1. 可能な場合は、依存関係をサブモジュールから外部に変換します。たとえば、viepagerインジケーターの場合、maven.orgリポジトリーにアイテムがあります-fr.avianey.com.viewpagerindicator:library。しかし、これでもまだ十分ではありません。関連するアイテムをproject.propertiesファイルのメインのsourceSetに追加する必要があります。詳細はこちら

例:

// add this dependency to your gradle file instead of project dependency
compile 'fr.avianey.com.viewpagerindicator:library:2.4.1@aar'

// add library dependencies for robolectric (now robolectric knows 
// about additional libraries to load resources)
android.library.reference.1=../../../app/build/intermediates/exploded-aar/fr.avianey.com.viewpagerindicator/library/2.4.1

このソリューションの差分はここで確認できます

  1. すべてのカスタムビューをメインアプリの下に移動します。単体テストのためだけにカスタムビューをアプリに移動するのは良いアプローチではありませんが、これによりError inflating class。の問題も修正されます。

私は最初の解決策を好みますが、プロジェクトの依存関係を外部に変更できない場合があります。

また、この問題についてRobolectricチームに報告します。

PS私はこの問題に関連するgithubのプロジェクトを持っています。


0

mTestRoboActivityView = LayoutInflater.from(new Activity()).inflate(R.layout.test, null);

このコード行で使用した「newActivity()」は、現在のアクティビティではなく、新しいアクティビティのインスタンスを意味します。この問題は、現在のアクティビティにインスタンスを渡すことで解決できます。このように使用-

public class TestRoboActivityTest {
private View mTestRoboActivityView;
private Context mContext;

public TestRoboActivityTest(Context mContext){
    this.mContext=mContext;
}

@Before
public void setUp() throws Exception {
    mTestRoboActivityView = (LayoutInflater.from(mContext)).inflate(R.layout.test, null);
}

@After
public void tearDown() throws Exception {
    mTestRoboActivityView = null;
}

@Test
public void testSanity() throws Exception {
    Assert.assertNotNull(mTestRoboActivityView);
}}

上記のコードが正常に機能するかどうかはわかりませんが、現在のアクティビティのインスタンスを参照するために使用してください。それがあなたを助けるかもしれないことを参照してください。


0

Roboelectricは完全なAndroidフレームワークを使用していないため、ビューを拡張することはできませんが、代わりにすべてのAndroidAPIをモックアウトします。

実際のビュー表示動作をテストするためにroboelectricを使用しないでください。単体テストに使用され、図面や表示などを表示するのではなく、ビジネスロジックをテストするだけです。これを実現するには、プログラムでビューオブジェクトを作成し、Androidシステムを必要とする特定の部分をモックアウトできます(MockitoやPowermockなどを使用) 。例:ロボエレクトリックでの単純なビューテスト:

MyCustomView view = new MyCustomView();
assertNotNull(view.setSomeNo(2);
assertTrue(2, view.getSomeNo());

また、ビューの外観やレンダリングなどのレンダリングをテストする場合は、実際のデバイスで実行されるEspressoRobotiumなどの機能テストフレームワークを使用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.