Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mockkStatic(Instant::class) failed with stdObjectAnswer than equals/hashCode/toString with Spring Boot 3.0.x and JDK 17.0.x #1075

Open
nico-arianto opened this issue Apr 5, 2023 · 10 comments

Comments

@nico-arianto
Copy link

Just be aware about usual MockK support pattern.
Tickets are checked from time to time, replied, discussed, labeled, e.t.c.
But real fixes are applied in a month-two month period in a bunch.
If you think this is unacceptable, go on, join the project, change the world.

Please remove sections wisely

Below information is actually needed to make all the process of fixing faster.
Choose main points. Don't blindly follow this as a set of rules.
Don't waste much time. Usually, the main thing is to have a good reproducible minimal code.

Prerequisites

Please answer the following questions for yourself before submitting an issue.

  • [ X] I am running the latest version
  • [ X] I checked the documentation and found no answer
  • [ X] I checked to make sure that this issue has not already been filed

Expected Behavior

Please describe the behavior you are expecting
This piece of code should work correctly

        val instant = Instant.ofEpochSecond(1680707823L)
        mockkStatic(Instant::class) {
            every { Instant.now() } returns instant
            assertThat(Instant.now()).isEqualTo(instant)
        }

Current Behavior

What is the current behavior?
Failed with error No other calls allowed in stdObjectAnswer than equals/hashCode/toString

Failure Information (for bugs)

Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.

No other calls allowed in stdObjectAnswer than equals/hashCode/toString
io.mockk.MockKException: No other calls allowed in stdObjectAnswer than equals/hashCode/toString
	at app//io.mockk.impl.stub.MockKStub.stdObjectAnswer(MockKStub.kt:75)
	at app//io.mockk.impl.recording.states.SafeLoggingState.call(SafeLoggingState.kt:9)
	at app//io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
	at app//io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:268)
	at app//io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:24)
	at app//io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:21)
	at java.base@17.0.6/java.time.Instant.ofEpochSecond(Unknown Source)
	at java.base@17.0.6/java.time.Clock.currentInstant(Clock.java:527)
	at java.base@17.0.6/java.time.Clock$SystemClock.instant(Clock.java:614)

Steps to Reproduce

Please provide detailed steps for reproducing the issue.

  1. It's working perfectly in Spring Boot 2.7.10
  2. Upgrade the Spring Boot to 3.0.5
  3. it's started to failed with this No other calls allowed in stdObjectAnswer than equals/hashCode/toString

Context

Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.

  • MockK version: 1.13.4
  • OS: macOS Ventura 13.3
  • Kotlin version: 1.7.22
  • JDK version: openjdk 17.0.6 2023-01-17
  • JUnit version: jupiter 5.9.2
  • Type of test: unit test

Stack trace

// -----------------------[ YOUR STACK STARTS HERE ] -----------------------
No other calls allowed in stdObjectAnswer than equals/hashCode/toString
io.mockk.MockKException: No other calls allowed in stdObjectAnswer than equals/hashCode/toString
	at app//io.mockk.impl.stub.MockKStub.stdObjectAnswer(MockKStub.kt:75)
	at app//io.mockk.impl.recording.states.SafeLoggingState.call(SafeLoggingState.kt:9)
	at app//io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
	at app//io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:268)
	at app//io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:24)
	at app//io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:21)
	at java.base@17.0.6/java.time.Instant.ofEpochSecond(Unknown Source)
	at java.base@17.0.6/java.time.Clock.currentInstant(Clock.java:527)
	at java.base@17.0.6/java.time.Clock$SystemClock.instant(Clock.java:614)
	at app//ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:130)
	at app//ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:424)
	at app//ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:386)
	at app//ch.qos.logback.classic.Logger.debug(Logger.java:487)
	at app//io.mockk.impl.log.Slf4jLogger.debug(Slf4jLogger.kt:15)
	at app//io.mockk.impl.log.SafeLogger$debug$1.invoke(SafeLogger.kt:45)
	at app//io.mockk.impl.log.SafeLogger$debug$1.invoke(SafeLogger.kt:44)
	at app//io.mockk.impl.recording.CommonCallRecorder.safeExec(CommonCallRecorder.kt:72)
	at app//io.mockk.impl.log.SafeLogger.safeLogging(SafeLogger.kt:68)
	at app//io.mockk.impl.log.SafeLogger.debug(SafeLogger.kt:44)
	at app//io.mockk.impl.instantiation.AbstractMockFactory.mockk(AbstractMockFactory.kt:53)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1$1.invoke(MockKStub.kt:198)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1$1.invoke(MockKStub.kt:197)
	at app//io.mockk.impl.InternalPlatform.customComputeIfAbsent(InternalPlatform.kt:44)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1.invoke(MockKStub.kt:197)
	at app//io.mockk.impl.recording.CommonCallRecorder.safeExec(CommonCallRecorder.kt:72)
	at app//io.mockk.impl.log.SafeToString.exec(SafeToString.kt:10)
	at app//io.mockk.impl.stub.MockKStub.childMockK(MockKStub.kt:196)
	at app//io.mockk.impl.recording.PermanentMocker.permamentize(PermanentMocker.kt:48)
	at app//io.mockk.impl.recording.PermanentMocker.mock(PermanentMocker.kt:24)
	at app//io.mockk.impl.recording.states.RecordingState.mockPermanently(RecordingState.kt:123)
	at app//io.mockk.impl.recording.states.RecordingState.round(RecordingState.kt:33)
	at app//io.mockk.impl.recording.CommonCallRecorder.round(CommonCallRecorder.kt:50)
	at app//io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:63)
	at app//io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
	at app//io.mockk.MockKDsl.internalEvery(API.kt:94)
	at app//io.mockk.MockKKt.every(MockK.kt:143)
	at app//io.tomoloyalty.identity.userblocks.query.BlockedIpQueryTest.tryToMockkInstant(BlockedIpQueryTest.kt:157)
	at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base@17.0.6/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@17.0.6/java.lang.reflect.Method.invoke(Method.java:568)
	at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
	at app//org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base@17.0.6/java.util.ArrayList.forEach(ArrayList.java:1511)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base@17.0.6/java.util.ArrayList.forEach(ArrayList.java:1511)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at app//org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:110)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:90)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:85)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
	at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base@17.0.6/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@17.0.6/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy2/jdk.proxy2.$Proxy5.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
// -----------------------[ YOUR STACK TRACE ENDS HERE ] -----------------------

Minimal reproducible code (the gist of this issue)

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
plugins {
  val kotlinVersion = "1.7.22"
  kotlin("jvm") version kotlinVersion
  kotlin("kapt") version kotlinVersion
  kotlin("plugin.spring") version kotlinVersion
  kotlin("plugin.jpa") version kotlinVersion
  id("org.springframework.boot") version "3.0.5"
  id("io.spring.dependency-management") version "1.1.0"
}
dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web")
  testImplementation("org.springframework.boot:spring-boot-starter-test")
  testImplementation("io.mockk:mockk:1.13.4")
}
dependencyManagement {
  mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
package io.mockk.gh

import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockkStatic
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.time.Instant

@ExtendWith(MockKExtension::class)
class InstantTest {

    @Test
    fun test() {
        val instant = Instant.ofEpochSecond(1680707823L)
        mockkStatic(Instant::class) {
            every { Instant.now() } returns instant
            assertThat(Instant.now()).isEqualTo(instant)
        }
    }

}
// -----------------------[ YOUR CODE ENDS HERE ] -----------------------
@nico-arianto nico-arianto changed the title mockkStatic failed with stdObjectAnswer than equals/hashCode/toString with Spring Boot 3.0.x and JDK 17.0.x mockkStatic(Instant::class) failed with stdObjectAnswer than equals/hashCode/toString with Spring Boot 3.0.x and JDK 17.0.x Apr 5, 2023
@roxid
Copy link

roxid commented Apr 12, 2023

We're experiencing the same problem when mocking Clock mockkStatic(Clock::class). Downgrading mockk to 1.13.3 works for now (with Spring Boot 3.0.5, Kotlin 1.8.10 and JDK 17.0.4.1).

@e-wag
Copy link

e-wag commented Apr 17, 2023

In our case it seemed to be an issue in combination with logback (version 1.4.5). When a LoggingEvent is being created Clock.systemUTC().instant() is getting called and then we are running into the issue described here.

As a workaround, we simply reduced the log level for the AbstractMockFactory and AnsweringState in our logback-test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Disable mockk logging to prevent stdObjectAnswer MockKException when mocking time -->
    <logger name="io.mockk.impl.instantiation.AbstractMockFactory" level="error"/>
    <logger name="io.mockk.impl.recording.states.AnsweringState" level="error"/>
</configuration>

@nico-arianto
Copy link
Author

@e-wag thanks, but it's not working for me.

@ademarao
Copy link

ademarao commented Jun 7, 2023

I got this error from a HasMap, I fixed by manually returning a map

val theMapToFix = mapOf<>()
val mockObj = mockk<> {
  every { getterOfTheMap } returns theMapToFix
}

@akoufa
Copy link

akoufa commented Jun 30, 2023

Unfortunately I am observing the same issue and the workaround from @e-wag unfortunately does not fix it

@IgnacioGarcia198
Copy link

I reproduced this with normal mocking, not mockstatic. And what I realized is that we are not supposed to use the mocked objects as if they were real objects.

@SamuelRuwe
Copy link

This isn't Spring related. I got the same bug today when I tried to use mockkStatic for Instant::class in a non-spring environment. (specifics are work related) I'm going to see if I can figure out what's going on and open a PR to fix it

@artemptushkin
Copy link

@SamuelRuwe any update on this? Currently, in spring boot apps we have to fall back to an old version because of some incompatibility with Spring Boot, it would be great if we can move forward and keep the version aligned with SB reference

@SamuelRuwe
Copy link

Hey @artemptushkin, I got side tracked with other things. I'll try to get some time this weekend to look into it. If you're using Spring one thing you could do is use indirection by creating a bean that provides Instant to your services that you can also mock out for testing. It's been a bit since I've done Spring, but something like the below example may fit your use case

    @Bean
    public InstantProvider instantProviderCreator() {
        return new InstantProviderImpl();
    }
    
    interface InstantProvider {
        Instant now();
    }

    class InstanceProviderImpl implements InstantProvider {
        @Override
       public Instant now() {
            return Instant.now();
        }
    }
    
    // in the tests
    class MockInstanceProvider implements InstantProvider {
    @Override
    public Instant now() {
        return Instant.EPOCH; (or any other arbitrary date you want for testing)
    }
}

@artemptushkin
Copy link

@SamuelRuwe as a workaround it's okay but I here care mostly about being up-to-date with the latest versions w/o a need to write a code. We want to be on the latest just to be aligned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants