I'm trying to run unit tests with Robolectric 2.1.1 and I cannot get it to inflate custom layouts (e.g. ViewPagerIndicator classes). Suppose this is my layout:
<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>
Consider this my test class:
@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); } }
Executing 'mvn clean test' results in
Tests in error: testSanity(TestRoboActivityTest): XML file .\res\layout\test.xml line #-1 (sorry, not yet implemented): Error inflating class com.viewpagerindicator.CirclePageIndicator
Cool, so it seems like custom views aren't supported yet. Checking the sample Robolectric project on their website, one solution could be to inflate the layout from 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); } }
which results in:
Tests in error: testSanity(TestRoboActivityTest): XML file .\res\layout\test.xml line #-1 (sorry, not yet implemented): Error inflating class com.viewpagerindicator.CirclePageIndicator
My last resort was trying to use shadow classes:
@Implements(CirclePageIndicator.class) public class CirclePageIndicatorShadow implements PageIndicator { @Override @Implementation public void setViewPager(ViewPager view) { // Stub } // etc. }
and using @Config(shadows = {CirclePageIndicatorShadow.class})
. This again resulted in
Tests in error: testSanity(TestRoboActivityTest): XML file .\res\layout\test.xml line #-1 (sorry, not yet implemented): Error inflating class com.viewpagerindicator.CirclePageIndicator
Edit (December 2014)
Please note that the following stracktrace was added later by David Rabinowitz. While related, it is not the issue I was facing at the time.
Here is the stack trace:
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
Could you guys please point me in the right direction? I'm out of ideas. Thanks.
I test views in the same test class with the Activity that uses them. In this case I tell Robolectric to give an instance of that Activity and from that I get an instance of the inflated view:
@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: I use Robolectric 3.0 so I am not sure if this applies to you.
This issue happens, because gradle merges project dependencies (ex: compile project(':lib-custom')
) and external dependencies (ex: compile 'lib.package:name:1.1.0'
) in different way. After dependencies were merged app has R.java
file with all resources fields (colors, ids, drawables, ...). But generated R.java
file looks different after merging submodules and external dependencies.
This problem exists only with projects, which have custom views in submodules. In case of external dependencies there is another issues, which can be easily fixed. Read about dependencies types here.
For project dependencies result R.java
file contains all resource identifiers, but identifiers from submodule doesn't equals to their original integer identifiers:
com.lib.custom.R.color.primary != com.main.project.R.color.primary
For external dependencies merged R.java
file just a merge result of R.java files from all external dependencies
com.lib.custom.R.color.primary == com.main.project.R.color.primary
I've found two possible solutions:
Example:
// 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
You can check diff for this solution here
Error inflating class
.I prefer first solution but it is not possible sometimes change project dependency to external.
I am also going to report about this issue to Robolectric team.
P.S. I have project on github related to this issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With