Макет статических методов Java с использованием Mockk
В настоящее время мы работаем с java с проектом kotlin, медленно переводя весь код в последний.
Можно ли издеваться над статическими методами, такими как Uri.parse()
с помощью Mockk?
Как будет выглядеть пример кода?
7 ответов
MockK позволяет высмеивать статические методы Java. Основной целью этого является насмешка над функциями расширения Kotlin, поэтому он не такой мощный, как PowerMock, но все же выполняет свою работу даже для статических методов Java.
Синтаксис будет следующим:
staticMockk<Uri>().use {
every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")
assertEquals(Uri("http", "test", "path"), Uri.parse("http://test/path"))
verify { Uri.parse("http://test/path") }
}
Подробнее здесь: http://mockk.io/
В дополнение к ответу oleksiyp:
Если вам нужно, чтобы это насмешливое поведение было всегда, не только в одном тестовом примере, вы можете смоделировать его, используя @Before
а также @After
:
@Before
fun mockAllUriInteractions() {
staticMockk<Uri>().mock()
every { Uri.parse("http://test/path") } returns Uri("http", "test", "path") //This line can also be in any @Test case
}
@After
fun unmockAllUriInteractions() {
staticMockk<Uri>().unmock()
}
Таким образом, если вы ожидаете, что больше элементов вашего класса будут использовать класс Uri, вы можете смоделировать его в одном месте, вместо того, чтобы загрязнять свой код .use
везде.
После макета 1.8.1:
Версия Mockk 1.8.1 устарела вышеупомянутое решение. После этой версии вы должны сделать:
@Before
fun mockAllUriInteractions() {
mockkStatic(Uri::class)
every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")
}
mockkStatic
будет очищаться при каждом вызове, поэтому вам больше не нужно его снимать
Остерегаться
Если вы позвоните
mockkSatic()
без блока не забудь позвонить
unmockkStatic()
после вызова издевательского метода. Метод не демонтируется автоматически, и вы все равно получите фиктивное значение даже в разных тестовых классах, которые не вызывают
mockkStatic()
, но используйте статический метод.
Другой вариант — выполнить фиктивный метод внутри блока, после чего он будет автоматически удален:
mockkStatic(Uri::class) {
every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")
val uri = Uri.parse("http://test/path")
}
Дополнительно к принятому ответу:
Вы не можете создать
Uri
вот так, вам придется издеваться над экземпляром Uri. Что-то вроде:
private val mockUri = mockk<Uri>()
@Before
fun mockAllUriInteractions() {
mockkStatic(Uri::class)
every { Uri.parse("http://test/path") } returns mockUri
// or just every { Uri.parse("http://test/path") } returns mockk<Uri>()
}
Если мы собираемся имитировать статику, например: mockkStatic(Klass::class)
тогда нам определенно нужно его размокнуть, например: unmockkStatic(Klass::class)
Я бы предложил размокнуть его в методе, аннотированном @After.
Полный пример:
class SomeTest {
private late var viewMode: SomeViewModel
@Before
fun setUp() {
viewMode = SomeViewModel()
mockkStatic(OurClassWithStaticMethods::class)
}
@After
fun tearDown() {
unmockkStatic(OurClassWithStaticMethods::class)
}
@Test
fun `Check that static method get() in the class OurClassWithStaticMethods was called`() {
//Given
every { OurClassWithStaticMethods.get<Any>(any()) } returns "dummyString"
//When
viewModel.doSomethingWhereStaticMethodIsCalled()
//Then
verify(exactly = 1) {
OurClassWithStaticMethods.get<Any>(any())
}
}
}
Этот пример написан с использованием мок-библиотеки " Mockk " v.1.12.0.
// Add @RunWith(RobolectricTestRunner::class) on top of
// your class since it provides access to Android framework APIs.
// Test case written inside the `mockkStatic` method to
// verify the behavior of a method that involves a static method call in Kotlin
@Test
fun `my test case`() = runBlocking {
// Mocking the Log class
mockkStatic(Log::class) {
// Test case to verify the behavior of a method
// that involves a log method call
}
}
// OR just use `mockkObject(Log)` without any block
Как уже упоминалось в нескольких ответах выше, вам необходимо убедиться, что вы вызываете unmockkStatic, иначе вы получите ненадежные тесты (поскольку имитируемый объект/функция будет доступен во всех тестовых классах.
В моем сценарии у меня была функция расширения для всего модуля в kotlin, и для издевательства я использовал сопутствующий объект, как показано ниже:
class SampleTest {
companion object {
@BeforeAll
@JvmStatic
fun setup() {
mockkStatic("packagename.filenameKt")
}
@AfterAll
@JvmStatic
fun teardown() {
unmockkStatic("packagename.filenameKt")
}
}}