【问题标题】:Error mocking Class which hold reference to SQLiteOpenHelper错误模拟包含对 SQLiteOpenHelper 的引用的类
【发布时间】:2017-12-12 09:00:07
【问题描述】:

我为我的 Presenter 编写单元测试,这需要模拟我的本地数据源

这是我的简单测试:

public class AddressPresenterTest {
    @Mock
    private AddressView mView;

    @Mock
    private AddressDataSource mDataSource;

    @Mock
    private AddressLocalDataSource mLocalDataSource;

    @Captor
    ArgumentCaptor<DataSourceCallback<Province>> mProvinceCallbackCaptor;

    private AddressPresenter mPresenter;

    @Before
    public void beforeTest() {
        MockitoAnnotations.initMocks(this);

        mPresenter = new AddressPresenter(mDataSource, mView);
        mPresenter.setLocalDataSource(mLocalDataSource);
    }

    @Test
    public void When_SelectProvince_DataIsNull_ShowErrorMessage() {
        mPresenter.getLocalProvinceById(2129023);

        // Cause data source has callback, we need to capture the callback
        ArgumentCaptor<Integer> provinceIdCaptor = ArgumentCaptor.forClass(Integer.class);
        verify(mLocalDataSource).fetchProvinceById(provinceIdCaptor.capture(), mProvinceCallbackCaptor.capture());
        mProvinceCallbackCaptor.getValue().onFailedLoad();

        verify(mView).loadContentError();
    }
}

当我运行这个测试时,我得到了错误,

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.dai.uangteman.data.AddressLocalDataSource.

Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.

Java               : 1.8
JVM vendor name    : Oracle Corporation
JVM vendor version : 25.77-b03
JVM name           : Java HotSpot(TM) 64-Bit Server VM
JVM version        : 1.8.0_77-b03
JVM info           : mixed mode
OS name            : Mac OS X
OS version         : 10.12.6

Underlying exception : java.lang.IllegalArgumentException: Could not create type

    at com.dai.uangteman.view.fragment.presenter.AddressPresenterTest.beforeTest(AddressPresenterTest.java:41)
...
Caused by: java.lang.IllegalArgumentException: Could not create type
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:140)
    ... 28 more
Caused by: java.lang.NoClassDefFoundError: android/database/sqlite/SQLiteOpenHelper
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    ... 45 more
Caused by: java.lang.ClassNotFoundException: android.database.sqlite.SQLiteOpenHelper
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    ... 87 more

当我改变我的 AddressLocalDataSource 课程时,我发现了一些奇怪的东西。

public class AddressLocalDataSource {
    private StaticDatabaseHelper mDatabaseHelper;

//    /**
//     * @param mDatabaseHelper
//     */
//    public AddressLocalDataSource(StaticDatabaseHelper mDatabaseHelper) {
//        this.mDatabaseHelper = mDatabaseHelper;
//    }

    public void setDatabaseHelper(StaticDatabaseHelper mDatabaseHelper) {
        this.mDatabaseHelper = mDatabaseHelper;
    }
    ...
}

低于我的发现:

  1. 我尝试删除包含StaticDatabaseHelper 对象参数的构造函数和setter 方法,它可以工作!
  2. 我尝试在 setter 方法中更改 StaticDatabaseHelper 初始化它不起作用!

问题是我的AddressLocalDataSource 需要StaticDatabaseHelper 类的实例。

有什么想法吗?谢谢你

【问题讨论】:

    标签: java android unit-testing mockito


    【解决方案1】:

    看来您还需要模拟 StaticDatabaseHelper。然后 Mockito 应该自动将您的模拟实例注入 AddressLocalDataSource。因此添加以下行可能会解决您的问题:

    public class AddressPresenterTest {
        @Mock
        StaticDatabaseHelper mockedDatabaseHelper;
        ...
    

    【讨论】:

      【解决方案2】:

      ✔ 回答

      在寻找解决方案几个小时后,我得到了最简单的解决方案。 我认为android库中有一些不完整的测试。所以我只是运行这个 gradle 命令来清理:

      ./gradlew clean test
      

      它现在可以工作了,在这种情况下,我只是模拟 StaticDatabaseHelper 类。 所以这是我最后的测试Class

      public class AddressPresenterTest {
      
          @Mock
          private AddressView mView;
      
          @Mock
          private AddressDataSource mDataSource;
      
          @Mock
          private AddressLocalDataSource mLocalDataSource;
      
          @Captor
          ArgumentCaptor<DataSourceCallback<Province>> mProvinceCallbackCaptor;
      
          private AddressPresenter mPresenter;
      
          @Before
          public void beforeTest() throws Exception {
              MockitoAnnotations.initMocks(this);
      
              mPresenter = new AddressPresenter(mDataSource, mView);
              mPresenter.setLocalDataSource(mLocalDataSource);
          }
      
          @Test
          public void When_SelectProvince_DataIsNull_ShowErrorMessage() {
              mPresenter.getLocalProvinceById(2129023);
      
              // Cause data source has callback, we need to capture the callback
              ArgumentCaptor<Integer> provinceIdCaptor = ArgumentCaptor.forClass(Integer.class);
              verify(mLocalDataSource).fetchProvinceById(provinceIdCaptor.capture(), mProvinceCallbackCaptor.capture());
              mProvinceCallbackCaptor.getValue().onFailedLoad();
      
              verify(mView).loadContentError();
          }
      }
      

      希望能帮到你,谢谢

      【讨论】:

        【解决方案3】:

        就我而言,在我的项目中使用 javassist-3.11.0.GA.jar。谷歌后,一些论文讨论了上述与 javassist 版本相关的问题,所以我尝试将 javassist 升级到 javassist-3.18.2-GA.jar 或 javassist-3.21.0-GA.jar 问题得到解决。

        其他一些问题可以通过以下方式解决:

        java.lang.VerifyError:分支目标处的堆栈图帧不一致 40 异常

        java.lang.RuntimeException: java.io.IOException: 无效常量 类型:18

        基础异常:java.lang.IllegalArgumentException:不能 创建类型

        问候,

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-13
          • 1970-01-01
          • 2012-02-02
          • 2015-05-24
          • 1970-01-01
          • 1970-01-01
          • 2021-11-14
          • 2017-11-21
          相关资源
          最近更新 更多