Error mocking Class which hold reference to SQLite

2020-04-16 05:35发布

I write unit test for my Presenter, which is need to mock my Local Data Source.

Here is my simple test :

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();
    }
}

When i run this test i got the error,

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

When i workaround to change my AddressLocalDataSource class i found something weird.

public class AddressLocalDataSource {
    private StaticDatabaseHelper mDatabaseHelper;

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

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

Below what i found :

  1. I try to remove my constructor and setter method which is contain StaticDatabaseHelper object parameter and it works!
  2. I try to change the StaticDatabaseHelper initialization in setter method it doesn't work!

The problem is my AddressLocalDataSource need instance of StaticDatabaseHelper class.

Any idea? thank you

3条回答
够拽才男人
2楼-- · 2020-04-16 05:52

Seems you also need to mock StaticDatabaseHelper. Then Mockito should automatically inject your mocked instance into the AddressLocalDataSource. So adding following line may solves your issue:

public class AddressPresenterTest {
    @Mock
    StaticDatabaseHelper mockedDatabaseHelper;
    ...
查看更多
欢心
3楼-- · 2020-04-16 05:59

✔ Answer

After a couple hour i looking for solution, i get the simplest solution. I think there is some incomplete in android library for testing. So i just run this gradle command to cleanup :

./gradlew clean test

And it works now, i just mock StaticDatabaseHelper class in this case. So this is my final testing 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();
    }
}

Hope this help, thank you

查看更多
狗以群分
4楼-- · 2020-04-16 06:08

In my case, in my project using javassist-3.11.0.GA.jar. After google some papers talk about the issue above related to javassist verison so I try to upgrade javassist to javassist-3.18.2-GA.jar or javassist-3.21.0-GA.jar the issue is resolved.

Some others issues can resolve by this way as below:

java.lang.VerifyError: Inconsistent stackmap frames at branch target 40 Exception

,

java.lang.RuntimeException: java.io.IOException: invalid constant type: 18

,

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

Regards,

查看更多
登录 后发表回答