Mockk не работает при выполнении всего тестового пакета Android

Я написал тестовые примеры для моей модели представления. Что, когда я запускаю индивидуально или когда я запускаю тестовый класс. Они успешно выполняются. Но когда я запускаю полный пакет androidTest, я получаю это исключение io.mockk.MockKException

Вот код, который успешно работает изолированно.

@RunWith(AndroidJUnit4::class)
class MyViewModelTest{

    @Test
    fun test_one(){
        getInstrumentation().runOnMainSync(Runnable {
            val context = ApplicationProvider.getApplicationContext<Context>()
            mockkStatic(MyManager::class)
            val myInterface = mockk<MyInterface>()
            every { MyManager.getCommunicator() } returns myInterface
            every { myInterface.context } returns context
            every { myInterface.getLongFromGTM(any()) } returns 0
            val viewModel = MyViewModel(context as Application)
            viewModel.model = MyDataModel()
            viewModel.model.isRepeatEligible = true
            val res = viewModel.isRepeatEligible()
            Truth.assertThat(res).isTrue()
        })
    }

}

Это ошибка, которую я получаю при запуске всего пакета androidTest:

Вот подробные используемые классы

1 .) MyManager.java

public class MyManager {
    private static MyInterface myCommunicator;
    
    public static MyInterface getCommunicator() {
        if (myCommunicator == null) {
            synchronized (MyManager.class) {
                if (myCommunicator == null) {
                    Class<?> cls = Class.forName("mypackage.communicator.MyCommunicator");
                    myCommunicator = (MyInterface) cls.newInstance();
                }
            }
        }
        return myCommunicator;
    }    
}

2.) MyViewModel.kt

class MyViewModel(application: Application) : BaseViewModel(application) {
    var model = MyDataModel()    
    private val timerDelay: Long by lazy {
        myCommunicator.getLongFromGTM("key_p2m_timer_delay")
    }
    val timerDuration: Long by lazy {
        myCommunicator.getLongFromGTM("key_p2m_timer_duration")
    }

    fun isRepeatEligible(): Boolean {
        model.apply {
            return isRepeatEligible && !isLinkBased && !isAlreadyPresent
        }
}

Откуда myCommunicator в MyViewModel? Это что-то определено в BaseViewModel?

Karsten Gabriel 07.12.2022 09:15

У нас есть отдельный класс функций расширения, где у нас есть этот экземпляр myCommunicator, который создается следующим образом: «val myCommunicator: MyInterface = MyManager.getCommunicator()»

Roop Kishore 07.12.2022 09:31

Это может быть (дополнительной) проблемой. Проверьте обновление в моем ответе.

Karsten Gabriel 07.12.2022 10:31
0
3
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Насмешка над чем-то с помощью MockK не ограничивается только одной функцией. В частности, когда вы имитируете объект с помощью mockkStatic, с этого момента объект будет макетом, пока он не будет удален с помощью unmockkStatic или unmockkAll.

В вашем случае, я думаю, проблема возникает из-за статического насмешливого MyManager, что позволяет последующим тестам не сработать, потому что они не ожидают, что объект будет смоделирован.

Это можно решить с помощью функции «после» (например, с помощью JUnit4, функции с аннотацией @After), которая вызывает unmockAll.

В качестве альтернативы, если вы хотите убедиться, что объект имитируется только локально, вы можете использовать вариант mockkStatic, который принимает блок, который является единственным местом, где объект имитируется следующим образом:

mockkStatic(MyManager::class) {
   // inside this block, MyManager is mocked
}
// MyManager is automatically unmocked after the block
Обновлять

Как упоминалось в вашем комментарии, вы не вызываете MyManager.getCommunicator() напрямую в MyViewModel, а через свойство расширения

val myCommunicator : MyInterface = MyManager.getCommunicator()

Это может привести к тому, что ваша тестовая установка останется на месте после теста, даже если вы разблокируете MyManager, потому что свойство myCommunicator сохранит свое значение — фиктивный интерфейс.

Это можно решить, изменив ваше свойство, чтобы оно не инициализировалось значением MyManager.getCommunicator(), но вместо этого вы должны определить геттер, который вызывает MyManager.getCommunicator():

val myCommunicator: MyInterface get() = MyManager.getCommunicator()

Таким образом, вы всегда получаете текущее значение MyManager.getCommunicator(), а не значение, которое было установлено один раз при инициализации.

См. https://kotlinlang.org/docs/properties.html#getters-and-setters для получения подробной информации о методах получения свойств.

Я попытался поместить код внутри этого блока mockkStatic, но все равно есть та же проблема.

Roop Kishore 07.12.2022 08:47

Другие вопросы по теме