I am trying to mock a sealed class which looks something like this:
sealed class Location
class Home: Location{
val name = "Home"
}
I would like to be able to do the following:
val mockHome = mockk<Home>() {
every { name } answers { "My Home" }
}
But unfortunately when I run this it fails with the following error:
io.mockk.MockKException: Missing calls inside every { ... } block.
What am I doing wrong?
For now you cannot. but this issue was fixed in this PR and we are waiting for next release of Mockk. you can track this PR for more information
Related
I am facing the same issue as asked in the below question. please help me out.
Mock a "global" property in Kotlin
I tried solution provided in above question but nothing is working. and I am asking the same question because I am not able to post any comment on the previous question.
I am trying to write test case for below class
class CustomLogger constructor(val ctx: Context, embEnabled: Boolean = false) : Logger {
private val loggers = arrayListOf<Logger>()
fun get() = loggers
init {
if (embEnabled)
loggers.add(Emb(ctx))
if (BuildConfig.DEBUG)
loggers.add(DebugLogger(ctx))
}
override fun logError(t: Throwable, msg: String?) {
loggers.forEach { logger ->
logger.logError(t, msg)
}
}
}
enter code here
Here I am trying to mock get() or init{}
that was on dam question but i got you
note this can only be used in unittest as mockito static mock is not support on Android JVM
testImplementation "org.mockito:mockito-inline:4.8.1" you gonna need
this so added
Update you need to call this i forgot to add it sorry in your test case before call the method
Mockito.mockStatic(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"))
fun getMockForMethod(clazz: Class<*>, methodName: String, methodResponse: Any) {
val method: Method = clazz.getMethod(methodName)
Mockito.`when`(method.invoke(null)).thenReturn(methodResponse)
}
now i created the method to handle no argument methods you can modifiy it as you see fit just pass the class using it name
getMockForMethod(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"),
"doSomething","New Response")
Assert.assertEquals("New Response", doSomething())
works like a charm Enjoy 😁
i have updated the above code for anyone to use with static members in kotlin
your updates makes this easy to do now it is a class that you can mock entirly and easliy mock any methods
val loggerMock= Mockito.mock(Logger::class.java)
Mockito.`when`(loggerMock.loggers).thenReturn(new array of loggers)
When running Robolectric unit tests with the latest version of Compose 1.2.0, then the tests using createAndroidComposeRule fail with the following error:
MyActivity has already set content. If you have populated the Activity with a ComposeView, make sure to call setContent on that ComposeView instead of on the test rule; and make sure that that call to setContent {} is done after the ComposeTestRule has run
Code from one of the failing tests:
composeTestRule.setContent {
Column {
Text(textTitle)
DemoScopedInjectedViewModelComposable()
}
}
Looking carefully, the error message helps a lot, even though it's talking about a ComposeView instead of an Activity. But according to it:
composeTestRule.setContent { ... }
should be changed to:
composeTestRule.activity.setContent { ... }
and the tests should run without this error occurring anymore.
Compose with 1.2 has forbidden to overwrite the content of the activity rule. But this can be still done with setting content directly on activity, not activity test rule.
The easiest way to solve this issue is to set content not on the activity rule but on the activity itself. It can be done like this:
composeTestRule.activity.runOnUiThread {
composeTestRule.activity.setContent {
Column {
Text(textTitle)
}
}
}
To simplify usage you can use following extension
fun <R : TestRule, A : ComponentActivity> AndroidComposeTestRule<R, A>.setContentOnActivity(
content: #Composable () -> Unit
) {
this.activity.runOnUiThread {
this.activity.setContent {
content()
}
}
}
The solution is to fetch the Compose View from the Activity (the Activity is available in the test rule), and then call setContent on that View instead of directly on the test rule, as the error message indicates.
Here is a test helper function I created to avoid this problem in my tests:
fun AndroidComposeTestRule<ActivityScenarioRule<MyActivity>, MyActivity>.clearAndSetContent(content: #Composable () -> Unit) {
(this.activity.findViewById<ViewGroup>(android.R.id.content)?.getChildAt(0) as? ComposeView)?.setContent(content)
?: this.setContent(content)
}
Updated test:
composeTestRule.clearAndSetContent {
Column {
Text(textTitle)
DemoScopedInjectedViewModelComposable()
}
}
For reference this is my test rule:
#get:Rule
val composeTestRule = createAndroidComposeRule<MyActivity>()
I'm running UI testing on Android devices using Appium. We recently migrated to JUnit5 and I'm attempting to utilize the #BeforeAll class to make sure the app is in a good state before we continue to the next class.
Currently, the tooltip in Android studio is indicating that the function is never used. In the log I'm seeing a junitException saying that the method must be static. I haven't implemented #TestInstance yet, I'd like to be able to use beforeAll without it for now. I'm just confused why it isn't working since my #beforeEach and #afterEach are both working. The error and code are below.
org.junit.platform.commons.JUnitException: #BeforeAll method 'public final void com.bypass.automation.BaseTest.healthcheck()' must be static unless the test class is annotated with #TestInstance(Lifecycle.PER_CLASS).
open class BaseTest {
lateinit var driver: AndroidDriver<MobileElement>
private val capabilities = DesiredCapabilities().apply {
setCapability(APPIUM_VERSION, "1.19.1")
setCapability(PLATFORM_NAME, "Android")
setCapability(DEVICE_NAME, "Android")
setCapability("appPackage", "com.ourpackage")
setCapability("appActivity", "com.ourpackage.PassthroughHomeActivity")
setCapability("automationName", "uiautomator2")
setCapability("skipDeviceInitialization", true)
setCapability("noReset", true)
setCapability("full-reset", false)
setCapability("enableMultiWindows", false)
setCapability("unlockType", "pin")
setCapability("unlockKey", "0000")
setCapability("newCommandTimeout", "120")
}
#BeforeAll
fun healthcheck() {
val currentActivity = driver.currentActivity()
println("Current activity is $currentActivity")
if (currentActivity.contains("StationSecurePayActivity")) {
println("Exiting Station Pay")
CreditCardEntryView(driver).clickBackButton()
}
when {
currentActivity.contains("kiosk") -> {
Thread.sleep(2000)
println("Exiting Kiosk")
KioskView(driver).exitKiosk()
println("Logging out")
LogInProviderUtil(driver).logOut()
}
currentActivity != ".LoginActivity" -> {
println("Logging out")
LogInProviderUtil(driver).logOut()
}
currentActivity.contains(".LoginActivity") -> {
println("Session was properly logged out. No action taken.")
}
}
}
#BeforeEach
fun setup() {
driver = AndroidDriver(URL("http://127.0.0.1:4750/wd/hub"), capabilities)
driver.manage()?.timeouts()?.implicitlyWait(30, SECONDS)
if (LogInProviderUtil(driver).isLoggedIn()){
LogInProviderUtil(driver).logOut()
}
}
#AfterEach
fun teardown() {
if (LogInProviderUtil(driver).isLoggedIn()){
LogInProviderUtil(driver).logOut()
driver.quit()
}
else {
driver.quit()
}
}
}
It will work. I believe that any method annotated with #BeforeAll must be static (unless the "per-class" test instance lifecycle is used). So it sounds to me like you should switch to that by adding this annotation to your test class: #TestInstance(Lifecycle.PER_CLASS)
Also, it is usual practice to make your setup and teardown methods public. Also, I recommend use of Selenium-Jupiter framework (https://github.com/bonigarcia/selenium-jupiter/blob/master/README.md#appium) . Good luck.
If you want to have an initialization block you may put it simply into
init{} method. And you don't have to annotate it.
I'm stuck trying to mock some stuff with mockk:
I have the following setup on gradle
root:
|-- App (just a sample app for the SDK)
|-- SDK (SDK we develop) << apply plugin: 'com.android.library'
|-- SDKimpl.kt
|-- Foo (wrapper around a .jar library) << apply plugin: 'com.android.library'
|-- Foo.kt
So I'm writing an androidTest for the SDK and trying to mock Foo.kt.
There's nothing unusual about Foo class, just direct class Foo(private val someParams) {
So using androidTestImplementation "io.mockk:mockk-android:1.8.13" the mock goes:
val mock: Foo = mockk()
// val mock: Foo = mockkClass(Foo::class) // also tried this
every { mock.getData() } returns listOf("1", "2", "3")
I'm always getting the following crash:
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:42)
Also tried just to gather information:
running inside JVM test folder. It gets mocked without issues, but I can't run my test as JVM
running androidTest inside Foo module. Got the same crash
using mockkClass(Foo::class). Got some crash
using annotation #MockK and MockKAnnotations.init(this). Got some crash.
added Log.d before every { line and inside getData() method and it seems the actual real method from the class is getting called during the mock setup. That seems super weird to me.
Any idea what's going wrong here?
edit:
as requested, full code. I'm current working on an isolated project to try to isolate the error, so Foo is just:
class Foo {
fun getData(): String {
Log.d(TAG, "invoked foo.getData()")
return "trolololo"
}
}
and then I have FooTest in androidTest:
class FooTest {
#Test
fun mock_foo() {
val foo = mockk<Foo>()
every { foo.getData() } returns "zero"
assertEquals("zero", foo.getData())
}
}
It seems to be a Mockk opened issue: https://github.com/mockk/mockk/issues/182
2 possible quick fixes (pick one):
Run the Instrumented Tests in an emulator >= Android-P
Set Foo class as open (and the method(s) you want to mock too)
Try to check the official guide and see what is missing.
In my case, I tried to mock an extension in Kotlin but missed the mockkStatic
fun Date.asMyTime() : DateTime = DateTime(this, DateTimeZone.getDefault())
mockkStatic("packageName.FileNameKt") // This is what I was missing
every {
DateTime().asMyTime()
} returns mock(DateTime::class.java)
In my case I forgot to spyk the class I was applying every {...} to. 😳
val presenter = spyk(MyPresenter())
every { view.myFun(any()) } returns Unit
In my case, I've missed
#Before
fun setUp() {
MockKAnnotations.init(this)
}
In my case I tried to mock using mock() function instead mockk() (double k)
Make sure the object is really a mock, not the real object.
For instance:
- Sdk sdk = Sdk()
+ Sdk sdk = mockk()
every { sdk.crypto } returns mockk()
My problem was that I used a java class without getters
public class KeyStorePasswordPair {
public KeyStore keyStore;
public String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
I needed to add getters for the variables to make mockking work:
public class KeyStorePasswordPair {
public KeyStore getKeyStore() {
return keyStore;
}
public String getKeyPassword() {
return keyPassword;
}
private KeyStore keyStore;
private String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
Mockk Missing calls inside every { ... } block
may also be thrown when you have an every block defined on an object that is not a mockk, for example:
You define stub behavior like this
every { foo.getData() } returns DATA
and then try to:
every { DATA.getVersion() } returns VERSION
Where MY_THING and VERSION objects are declared (and instantiated) in the test class.
The error message is not very informative and a bit misleading in that case.
try like this
`when`(mock.getData()).thenReturn(listOf("1", "2", "3"))
I'm using coroutines to do an asynchronous call on pull to refresh like so:
class DataFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
// other functions here
override fun onRefresh() {
loadDataAsync()
}
private fun loadDataAsync() = async(UI) {
swipeRefreshLayout?.isRefreshing = true
progressLayout?.showContent()
val data = async(CommonPool) {
service?.getData() // suspending function
}.await()
when {
data == null -> showError()
data.isEmpty() -> progressLayout?.showEmpty(null, parentActivity?.getString(R.string.no_data), null)
else -> {
dataAdapter?.updateData(data)
dataAdapter?.notifyDataSetChanged()
progressLayout?.showContent()
}
}
swipeRefreshLayout?.isRefreshing = false
}
}
Everything here works fine when I actually put it on a device. My error, empty, and data states are all handled well and the performance is good. However, I'm also trying to unit test it with Spek. My Spek test looks like this:
#RunWith(JUnitPlatform::class)
class DataFragmentTest : Spek({
describe("The DataFragment") {
var uut: DataFragment? = null
beforeEachTest {
uut = DataFragment()
}
// test other functions
describe("when onRefresh") {
beforeEachTest {
uut?.swipeRefreshLayout = mock()
uut?.onRefresh()
}
it("sets swipeRefreshLayout.isRefreshing to true") {
verify(uut?.swipeRefreshLayout)?.isRefreshing = true // says no interaction with mock
}
}
}
}
The test is failing because it says that there was no interaction with the uut?.swipeRefreshLayout mock. After some experimenting, it seems this is because I'm using the UI context via async(UI). If I make it just be a regular async, I can get the test to pass but then the app crashes because I'm modifying views outside of the UI thread.
Any ideas why this might be occurring? Also, if anyone has any better suggestions for doing this which will make it more testable, I'm all ears.
Thanks.
EDIT: Forgot to mention that I also tried wrapping the verify and the uut?.onRefresh() in a runBlocking, but I still had no success.
If you want to make things clean and consider using MVP architecture in the future you should understand that CourutineContext is external dependency, that should be injected via DI, or passed to your presenter. More details on topic.
The answer for your question is simple, you should use only Unconfined CourutineContext for your tests. (more)
To make things simple create an object e.g. Injection with:
package com.example
object Injection {
val uiContext : CourutineContext = UI
val bgContext : CourutineContext = CommonPool
}
and in test package create absolutely the same object but change to:
package com.example
object Injection {
val uiContext : CourutineContext = Unconfined
val bgContext : CourutineContext = Unconfined
}
and inside your class it will be something like:
val data = async(Injection.bgContext) {service?.getData()}.await()