PowerMock + Robolectric + Dagger2。第一部分

时间:2016-01-09 09:53:18

标签: android unit-testing robolectric dagger-2 powermockito


这个问题是从PowerMock + Robolectric + Dagger2的第一部分创建的 所以我再一次。抱歉。

我测试自定义视图类,其中包含:

  1. android ui elements
  2. 一些逻辑
  3. 静态方法调用
  4. dagger2依赖

  5. 所以我使用下一个工具进行测试

    1. 用于UI元素的Robolectric嘲笑
    2. 逻辑测试的单元测试
    3. PowerMock用于静态方法模拟
    4. Robolectric + PowerMock集成问题已知且解决方案已知 - https://github.com/robolectric/robolectric/wiki/Using-PowerMock
      但是使用这个解决方案,dagger2依赖会失败。

      注意代码
      我的自定义视图 - ProgressTextView

      public class ProgressTextView extends TextView {
      
          private String defaultText;
          private int fileSize;
          private String fileSizeString;
          private FileDownloaderI fileDownloader;
      
          @Inject
          FileDownloaderManager fileDownloaderManager;
      
          Subscription downloadProgressChannelSubscription;
          Subscription downloadCancelChannelSubscription;
      
          public ProgressTextView(Context context) {
              super(context);
              provideDependency();
          }
      
          public ProgressTextView(Context context, AttributeSet attrs) {
              super(context, attrs);
              provideDependency();
          }
      
          public ProgressTextView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
              provideDependency();
          }
      
          private void provideDependency() {
              ApplicationSIP.get().applicationComponent().inject(this);
          }
      
      }
      

      ApplicationSIP

      public class ApplicationSIP extends Application {
      
          public static volatile Context applicationContext;
      
          @NonNull
          private AppComponent appComponent;
      
          @Override
          public void onCreate() {
              super.onCreate();
              applicationContext = getApplicationContext();
              Logger.registerLogger(this);
      
              appComponent = prepareApplicationComponent().build();
              appComponent.inject(this);
          }
      
          @NonNull
          protected DaggerAppComponent.Builder prepareApplicationComponent() {
              return DaggerAppComponent.builder()
                      .appModule(new AppModule(getApplicationContext()))
                      .tGModule(new TGModule());
          }
      
          @NonNull
          public AppComponent applicationComponent() {
              return appComponent;
          }
      
          @NonNull
          public static ApplicationSIP get() {
              return (ApplicationSIP) applicationContext.getApplicationContext();
          }
      
      }
      

      AppComponent

      @Component(modules = {AppModule.class, TGModule.class, MediaModule.class, StaticModule.class})
      @Singleton
      public interface AppComponent {   
          void inject(ApplicationSIP applicationSIP);
          void inject(ProgressDownloadView progressDownloadView);
          void inject(ProgressTextView progressTextView);
      }
      

      ProgressTextViewTest

      @RunWith(RobolectricUnitTestRunner.class)
      @PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
      @PrepareForTest(Formatter.class)
      public class ProgressTextViewTest {
      
          @Rule
          public PowerMockRule rule = new PowerMockRule();
          Activity activity;
      
          @Before
          public void beforeTest() {
              // PowerMockito
              PowerMockito.mockStatic(Formatter.class);
              Mockito.when(Formatter.formatFileSize(anyObject(), anyInt())).thenReturn("");
              // Robolectic
              activity = Robolectric.setupActivity(Activity.class);
          }
      
          @Test
          public void init_FileDownloaded() {
              ProgressTextView progressTextView = new ProgressTextView(activity);
              ...
          }
      }
      

      所以当我开始这个测试时,我得到下一个例外:

      java.lang.NullPointerException
      at com.tg.osip.ApplicationSIP.get(ApplicationSIP.java:64)
      at com.tg.osip.ui.general.views.ProgressTextView.provideDependency(ProgressTextView.java:60)
      at com.tg.osip.ui.general.views.ProgressTextView.<init>(ProgressTextView.java:46)
      at com.tg.osip.ui.general.views.ProgressTextViewTest.init_FileDownloaded(ProgressTextViewTest.java:67)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
      at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
      at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
      at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
      at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
      at org.powermock.modules.junit4.rule.PowerMockStatement$1.run(PowerMockRule.java:52)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at org.powermock.reflect.internal.WhiteboxImpl.performMethodInvocation(WhiteboxImpl.java:1873)
      at org.powermock.reflect.internal.WhiteboxImpl.doInvokeMethod(WhiteboxImpl.java:773)
      at org.powermock.reflect.internal.WhiteboxImpl.invokeMethod(WhiteboxImpl.java:638)
      at org.powermock.reflect.Whitebox.invokeMethod(Whitebox.java:401)
      at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:98)
      at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:78)
      at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:49)
      at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
      at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
      at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
      at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
      at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
      at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
      at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
      at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
      at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
      at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
      at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
      at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
      at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
      at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
      

      如果更改robolectric活动初始化:

      @RunWith(RobolectricUnitTestRunner.class)
      @PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
      @PrepareForTest(Formatter.class)
      public class ProgressTextViewTest {
      
          @Rule
          public PowerMockRule rule = new PowerMockRule();
          Activity activity = Robolectric.setupActivity(Activity.class);
      
          @Before
          public void beforeTest() {
              // PowerMockito
              PowerMockito.mockStatic(Formatter.class);
              Mockito.when(Formatter.formatFileSize(anyObject(), anyInt())).thenReturn("");
          }
      
          @Test
          public void init_FileDownloaded() {
              ProgressTextView progressTextView = new ProgressTextView(activity);
              ...
          }
      }
      

      我得到其他例外:

      java.lang.OutOfMemoryError: Java heap space
      at java.util.Arrays.copyOf(Arrays.java:3332)
      at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)
      at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)
      at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:569)
      at java.lang.StringBuffer.append(StringBuffer.java:369)
      at java.io.StringWriter.write(StringWriter.java:94)
      at com.thoughtworks.xstream.core.util.QuickWriter.flush(QuickWriter.java:73)
      at com.thoughtworks.xstream.io.xml.PrettyPrintWriter.flush(PrettyPrintWriter.java:342)
      at com.thoughtworks.xstream.XStream.toXML(XStream.java:858)
      at com.thoughtworks.xstream.XStream.toXML(XStream.java:843)
      at org.powermock.classloading.DeepCloner.clone(DeepCloner.java:53)
      at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:89)
      at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:78)
      at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:49)
      at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
      at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
      at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
      at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
      at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
      at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
      at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
      at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
      at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
      at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
      at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
      at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      

2 个答案:

答案 0 :(得分:2)

在回答你的问题之前。我会说:

  1. 你的观点是做得/知道得太多了。我正在谈论FileDownloaderManager的知识和关于如何获得依赖关系的知识
  2. 由于您正在使用注入,我会将静态函数包装到类中并注入它。编写/维护测试会更容易
  3. 甚至班级AppComponent的名称都表示它不应该知道如何注入视图。考虑范围注射
  4. 一些applicationContext的未成年人。为什么它是volatile,您是否希望从非主线程访问它?已经ApplicationSIP为什么你需要玩应用程序上下文和转换?
  5. 最后回答。第一个堆栈跟踪表明appContext为空。因此,您的应用的onCreate未被调用。我没有看到RobolectricUnitTestRunner的代码,但我认为问题在于 PowerMock 的使用。第二个堆栈跟踪那种证明。所以我想说的是停止使用 PowerMock

答案 1 :(得分:1)

<强>摘要
不要将PowerMock与Robolectric和Dagger2一起使用。这不安全 现在我使用静态方法的包装类,并使用Dagger2帮助注入这些类。所以我的测试类中没有静态方法,PowerMock也不需要。
我认为这是很正常的变种 并感谢Eugen Martynov的帮助。