Robolectric으로 사용자 지정보기 테스트
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
저를 올바른 방향으로 안내해 주시겠습니까? 나는 아이디어가 없습니다. 감사.
문제:
이 문제는 gradle이 프로젝트 종속성 (예 :) compile project(':lib-custom')
과 외부 종속성 (예 :) compile 'lib.package:name:1.1.0'
을 다른 방식으로 병합하기 때문에 발생합니다 . 종속성이 병합 된 후 앱에는 R.java
모든 리소스 필드 (색상, ID, 드로어 블 등) 가있는 파일 이 있습니다. 그러나 생성 된 R.java
파일은 하위 모듈과 외부 종속성을 병합 한 후 다르게 보입니다.
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
Solution:
I've found two possible solutions:
- Convert your dependencies from submodule to external where possible. For example for viepager indicator has an item in maven.org repository - fr.avianey.com.viewpagerindicator:library. But this is still not enough - you need to add related item to project.properties file to your main sourceSet. More info here
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
- Move all your custom views under your main app. It is not good approach to move Custom views to app only because of unit testing, but this will also fix issue with
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.
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.
mTestRoboActivityView = LayoutInflater.from(new Activity()).inflate(R.layout.test, null);
In this line of code you used 'new Activity()' means instance of new Activity, that not for your current Activity. You can resolve this issue by passing instance on current Activity. Use like this-
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);
}}
I am not sure that above code working fine but use for reference, instance of current Activity. Refer it may be help you.
You cannot inflate views in Roboelectric since it does not use the complete android framework but instead mocks out all the Android API's.
You should not use roboelectric to test actual view display behavior. It is to be used for unit tests and just to test your business logic and not view drawing/display etc. To achieve that You can programmatically create view objects and mock out certain parts which need the android system (Use something like Mockito or Powermock). eg of simple view testing in roboelectic:
MyCustomView view = new MyCustomView();
assertNotNull(view.setSomeNo(2);
assertTrue(2, view.getSomeNo());
Also if you want to test rendering of how your view looks or renders etc, you should use functional testing frameworks such as Espresso or Robotium which run on an actual device.
참고URL : https://stackoverflow.com/questions/18213952/testing-custom-views-with-robolectric
'program tip' 카테고리의 다른 글
Python 스크립트 내에서 UAC 상승을 요청 하시겠습니까? (0) | 2020.10.07 |
---|---|
Delphi XML 데이터 바인딩 마법사를 사용할 때 필요한 태그가 없습니다. (0) | 2020.10.06 |
Android AudioRecord가 다른 스트림을 MIC 오디오 소스로 강제 (0) | 2020.10.06 |
SBT 및 IntelliJ IDEA로 여러 상호 의존적 모듈을 관리하는 방법은 무엇입니까? (0) | 2020.10.06 |
CSS에서 텍스트와 선택 상자를 같은 너비로 정렬 하시겠습니까? (0) | 2020.10.06 |