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 프로젝트를 확인하면 한 가지 해결책은 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는 David Rabinowitz가 나중에 추가했습니다. 관련이 있지만 당시에 직면 한 문제는 아닙니다.
다음은 스택 추적입니다.
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
저를 올바른 방향으로 안내해 주시겠습니까? 나는 아이디어가 없습니다. 감사.
답변
동일한 테스트 클래스에서 뷰를 사용하는 활동으로 뷰를 테스트합니다. 이 경우 Robolectric에게 해당 Activity의 인스턴스를 제공하도록 지시하고 그로부터 부풀린보기의 인스턴스를 얻습니다.
@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을 사용하므로 이것이 귀하에게 해당되는지 확실하지 않습니다.
답변
문제:
이 문제는 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
해결책:
두 가지 가능한 해결책을 찾았습니다.
- 가능한 경우 종속성을 하위 모듈에서 외부로 변환하십시오. 예를 들어 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
여기 에서이 솔루션의 차이점을 확인할 수 있습니다.
- 모든 사용자 지정보기를 기본 앱 아래로 이동합니다. 단위 테스트 때문에 사용자 지정보기를 앱으로 이동하는 것은 좋은 접근 방식이 아니지만
Error inflating class
.
나는 첫 번째 솔루션을 선호하지만 때로는 프로젝트 종속성을 외부로 변경할 수 없습니다.
이 문제에 대해 Robolectric 팀에도보고 할 것입니다.
추신 : 이 문제와 관련된 github에 프로젝트가 있습니다.
답변
mTestRoboActivityView = LayoutInflater.from(new Activity()).inflate(R.layout.test, null);
이 코드 줄에서 사용한 ‘new Activity ()’는 현재 Activity가 아닌 새로운 Activity의 인스턴스를 의미합니다. 현재 활동에서 인스턴스를 전달하여이 문제를 해결할 수 있습니다. 이렇게 사용-
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);
}}
위의 코드가 잘 작동하는지 확실하지 않지만 현재 활동의 인스턴스를 참조로 사용합니다. 도움이 될 수 있습니다.
답변
Roboelectric에서는 완전한 Android 프레임 워크를 사용하지 않고 대신 모든 Android API를 모방하기 때문에 뷰를 확장 할 수 없습니다.
실제보기 표시 동작을 테스트하기 위해 roboelectric을 사용해서는 안됩니다. 단위 테스트에 사용되며 그리기 / 디스플레이 등을 보지 않고 비즈니스 로직을 테스트하는 데 사용됩니다.이를 달성하기 위해 프로그래밍 방식으로보기 개체를 만들고 Android 시스템이 필요한 특정 부분을 조롱 할 수 있습니다 ( Mockito 또는 Powermock 같은 것을 사용 ). . 예를 들어 roboelectic에서 간단한보기 테스트 :
MyCustomView view = new MyCustomView();
assertNotNull(view.setSomeNo(2);
assertTrue(2, view.getSomeNo());
또한 뷰가 어떻게 보이거나 렌더링되는지 등의 렌더링을 테스트 하려면 실제 장치에서 실행되는 Espresso 또는 Robotium 과 같은 기능 테스트 프레임 워크를 사용해야합니다 .