I have a method to download content from streaming end point. I want to write unit test this method. What I faced a problem is I need to mock kotlin extension method file.outputStream()
but I can't mock
outputStream()
which is defined in kotlin-stdlib
FileReadWrite
File as like below
@kotlin.internal.InlineOnly
public inline fun File.outputStream(): FileOutputStream {
return FileOutputStream(this)
}
My download method is like below
override fun download(url: String, targetFile: File): Single<File> {
return Single.fromCallable {
fileDownloadApi
.downloadFile(url)
.execute()
.body()?.byteStream()?.use { input ->
targetFile.outputStream().use { output ->
FileUtil.copyFile(input, output)
return@fromCallable targetFile
}
}
targetFile
}
}
I need to mock this part targetFile.outputStream()
.
I am passing mock targetFile
My test code is like bolow
val mockFileOutputStream = mockk<FileOutputStream>()
val mockTargetFile = mockk<File>()
mockkStatic("kotlin.io.FilesKt") // declares as JvmName @file:JvmName("FilesKt")
every { File("abc").outputStream() } returns mockFileOutputStream
service.download("", mockTargetFile)
.subscribe(testObserver)
I mocked file.outputStream()
like official doc suggested but getting error
io.mockk.MockKException: Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock
for line every { File("abc").outputStream() } returns mockFileOutputStream
I can't figureout what i'm missing. Please help me to figure out this issue.
Notice that the extension function is inline
. This means that when you call it, there is no actual call to the extension method declared in FilesKt
. Instead, the compiler generates code that directly calls the constructor of FileOutputStream
, since that is the implementation of outputStream
.
So what you should be mocking is the constructor of FileOutputStream
, not FilesKt
.
For example,
mockkConstructor(FileOutputStream::class) {
// write the mocks that you would have written for mockFileOutputStream here
every { anyConstructed<FileOutputStream>().write(...) } just runs
// and you don't need mockFileOutputStream anymore
}