'Mock Bigquery function in kotlin unit test
I am develop an unit test, which involved (com.google.api.services.bigquery.Bigquery). I mock the object with mockk @SpyK. But the every{} block report error when unit test start. Detail as follow
the dependency is:
"com.google.apis:google-api-services-bigquery:v2-rev20220326-1.32.1"
The exception is throw in setUp() function
Code
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(MockKExtension::class, SpringExtension::class)
@EnableConfigurationProperties(ConfidentialTags::class)
@PropertySource(value = ["classpath:application.yml"], factory = YamlPropertySourceFactory::class)
class ResourceAccessServiceTest {
private val mockGoogleCredential = MockGoogleCredential.Builder().build()
@SpyK
private var olderBigQuery: Bigquery = Bigquery.Builder(
MockGoogleCredential.newMockHttpTransportWithSampleTokenResponse(),
mockGoogleCredential.jsonFactory,
mockGoogleCredential
).build()
@InjectMockKs
private lateinit var resourceAccessService: ResourceAccessService
@BeforeAll
fun setUp() {
//error reported here
every { olderBigQuery.RowAccessPolicies().list(any(), any(), any()).execute() } returns mockk() {
every { rowAccessPolicies } returns listOf()
}
}
@Test
fun queryRowAccessExistedPrincipals() {
val list = resourceAccessService.queryRowAccessExistedPrincipals(
TableId.of("proj", "ds", "tbl"),
RowFilter("region", listOf("hk"))
)
assert(list.isEmpty())
}
}
error log
java.net.MalformedURLException: no protocol: projects/2af774bb308513d5/datasets/-3e5359fe6b1a603f/tables/-28b905709f94ecc7/rowAccessPolicies
java.lang.IllegalArgumentException: java.net.MalformedURLException: no protocol: projects/2af774bb308513d5/datasets/-3e5359fe6b1a603f/tables/-28b905709f94ecc7/rowAccessPolicies
at com.google.api.client.http.GenericUrl.parseURL(GenericUrl.java:679)
at com.google.api.client.http.GenericUrl.<init>(GenericUrl.java:125)
at com.google.api.client.http.GenericUrl.<init>(GenericUrl.java:108)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequestUrl(AbstractGoogleClientRequest.java:373)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequest(AbstractGoogleClientRequest.java:404)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:514)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:455)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:565)
at com.airwallex.data.grpc.das.service.ResourceAccessServiceTest$setUp$1.invoke(ResourceAccessServiceTest.kt:67)
at com.airwallex.data.grpc.das.service.ResourceAccessServiceTest$setUp$1.invoke(ResourceAccessServiceTest.kt:67)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
at io.mockk.MockKDsl.internalEvery(API.kt:93)
at io.mockk.MockKKt.every(MockK.kt:98)
at com.airwallex.data.grpc.das.service.ResourceAccessServiceTest.setUp(ResourceAccessServiceTest.kt:67)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:126)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeAllMethod(TimeoutExtension.java:68)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllMethods$9(ClassBasedTestDescriptor.java:384)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllMethods(ClassBasedTestDescriptor.java:382)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:196)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:78)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:136)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
The key error info is
java.net.MalformedURLException: no protocol: projects/2af774bb308513d5/datasets/-3e5359fe6b1a603f/tables/-28b905709f94ecc7/rowAccessPolicies
The exception is thrown in the every{} block in setUp function. It seems the url built inside the SDK is invalid, without protocol header. But I don't know how to solve the problems
Solution 1:[1]
So what you really want is a way to decouple the code under test from BigQuery. The better way to do so that is to hide BigQuery behind your own abstraction (e.g. an interface) and make the code that needs to query only depend on that abstraction - look up the dependency inversion principle/dependency injection (I've got some answers here that show examples). That way, you can substitute a fake implementation in for testing that, say, queries against a collection in memory.
There is still one problem with this approach. You do at some point need to test against BigQuery for real. Otherwise, how will you prove that component works? I guess the possibilities here are constrained by what your company allows.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ndc85430 |
