MockK capturing a suspend lambda / coroutine - unit-testing

I am trying to capture a suspend lambda / coroutine and invoke it, but the mocking call just hangs.
#Test
fun test() = coroutinesTestRule.testDispatcher.runBlockingTest {
coEvery { db.withTransaction(captureCoroutine<suspend () -> Unit>()) } answers {
coroutine<suspend () -> Unit>().coInvoke()
}
}
Any hints or tips if I'm doing something wrong or is this just impossible?

I believe you need to use coAnswers:
#Test
fun test() = coroutinesTestRule.testDispatcher.runBlockingTest {
coEvery {
db.withTransaction(captureCoroutine<suspend () -> Unit>())
} coAnswers {
coroutine<suspend () -> Unit>().coInvoke()
}
}

Related

Rust cache async traits

I'm running into an issue when I attempt to cache a value for as long as it's valid and update it when it becomes invalid. I believe the issue is due to my attempt to share state across async executions. Further, this component lives in a multi-threaded / concurrent environment.
The error I'm seeing that I don't know how to fix is
future is not `Send` as this value is used across an await
Following is a minimum example that I could come up with (it also features some ownership issues) that generally captures my use-case and the issue I'm seeing. Here is a playground of the code.
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::{Arc, Mutex};
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mg = self
.cached_creds
.lock()
.expect("Unable to get lock on creds mutex");
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.unwrap();
}
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds);
return new_creds;
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}
I wasn't sure how to include this in the example but in main imagine multiple worker threads running concurrently / parallel that each call CredsProvider.get_creds and then use these creds to perform some work (if you can add that to a complete working example, that'd be much appreciated for my edification). Assume MyCredsProvider.inner_get_creds is expensive and should only be called when the cached creds expire.
How do I solve this? I thought that the Arc<Mutex<>> would be enough but it seems not. At one point, I tried making Creds and trait so that I could have Arc<Mutex<Option<Box<dyn Creds + Send + Sync>>>> but that felt like the wrong path and didn't work.
Thanks.
You may would like to switch to tokio::sync::Mutex (playground).
It solves
future is not `Send` as this value is used across an await
Code:
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mut mg = self
.cached_creds
.lock()
.await;
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.clone().unwrap();
} else {
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds.clone());
return new_creds;
}
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}

Kotlin Mockk test for suspend cancellable coroutine cancellation

I have classes
// final class from some library like okhttp
class NetworkCaller {
fun call() {
// performs some real operation
}
fun cancel() {
// .... cancels the request
}
}
class Request {
suspend fun asyncRequest(): String = suspendCancellableCoroutine { continuation ->
val call = NetworkCaller()
continuation.invokeOnCancellation {
call.cancel() // i need to write a test to mock if call.cancel is getting called or not
}
// rest of the code...
}
}
When i am doing
#Test
fun testRequestCancellation() {
val request = Request()
val job = GlobalScope.launch {
val response = request.asyncRequest()
println(response)
}
runBlocking {
job.cancel()
job.join()
}
}
The job is getting cancelled and continuation.invokeOnCancellation() is getting called, i checked with println statements. But i want to mock if the call.cancel method is getting called or not, using mockk library.
I am stuck on this, need help.
In your class, expose the NetworkCaller so it can be switched out for a mock during testing:
class Request(val call: NetworkCaller = NetworkCaller()) {
suspend fun asyncRequest(): String = suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
call.cancel() // i need to write a test to mock if call.cancel is getting called or not
}
// rest of the code...
}
}
Then you can use mockk in your test:
#Test
fun testRequestCancellation() {
val mockkCall = mockk<NetworkCaller> {
coEvery { cancel() } just Runs
}
val request = Request(mockkCall)
val job = GlobalScope.launch {
val response = request.asyncRequest()
println(response)
}
runBlocking {
job.cancel()
job.join()
}
coVerify { mockkCall.cancel() }
confirmVerified(mockkCall)
}

How to write unit tests for asynchronous function with a callback argument

I'm writing common tests for my kotlin multiplatform library which implements the API business logic using ktor client library.
I have a function which takes a callback as an argument, use coroutines to make the request to the API, and then execute the callback.
Here is a simplified version of the function from my UserApi class I want to test
fun <T : Any> fetch(
requestBuilder: HttpRequestBuilder,
deserializer: DeserializationStrategy<T>,
callback: (Either<ErrorMessage, T>) -> Unit)
{
GlobalScope.launch(dispatcherIO) {
val result: Either<ErrorMessage, T> =
try {
val returnObject: T = Json.parse(
deserializer,
HttpClient().post(requestBuilder)
)
Either.Right(returnObject)
} catch (e: Exception) {
Either.Left(ErrorMessage(e.message))
}
withContext(dispatcherMain) { callback(result) }
}
}
I would like to write a unit test like that:
#Test
fun requestOK() {
runTest { //runTest returns a platform specific runBlocking
val UserApi().fetch(request, User.serializer()) {
it.fold(
{ failure -> fail("must return success" },
{ user -> assertEquals(expectedUser, user) }
)
}
}
}

How do I suppress output from multiple threads when running cargo test?

If I execute the below testcases with cargo test, the output of one_thread_test will be suppressed as stated in the documentation.
However the output from multi_thread_test will appear on stdout. Is it possible to match the behavior of single- and multi-threaded testcases?
#[test]
fn one_thread_test() {
println!("A");
println!("B");
}
#[test]
fn multi_thread_test() {
use std::thread;
let mut threads = vec![];
for _ in 0..100 {
let t = thread::spawn(move || {
println!("from thread");
});
threads.push(t);
}
for thread in threads {
thread.join().unwrap();
}
}
Here is a quick-and-dirty workaround.
It works by sending messages to a receiver owned by a struct in the main thread. The receiver prints all of the accumulated messages when it is dropped - this is important so that panics caused by failed assertions don't prevent the printing.
use std::sync::mpsc::{channel, Sender, Receiver};
struct TestPrinter {
receiver: Receiver<String>,
sender: Sender<String>,
}
impl TestPrinter {
fn new() -> TestPrinter {
let (sender, receiver) = channel();
TestPrinter { receiver, sender }
}
fn sender(&self) -> Sender<String> {
self.sender.clone()
}
}
impl Drop for TestPrinter {
fn drop(&mut self) {
while let Some(v) = self.receiver.try_recv().ok() {
println!("later: {}", v);
}
}
}
And a convenience macro so it feels mostly like calling println!:
macro_rules! myprint {
($send: expr, $($arg:tt)*) => {
(*&$send).send(format!($($arg)*));
};
}
In order to send messages for printing, you have get a sender for each thread:
#[test]
fn multi_thread_test() {
use std::thread;
let mut threads = vec![];
let printer = TestPrinter::new();
for _ in 0..100 {
let sender = printer.sender();
let t = thread::spawn(move || {
myprint!(sender, "from thread");
});
threads.push(t);
}
for thread in threads {
thread.join().unwrap();
}
}
The actual printing happens when printer goes out of scope. It's in the main thread so it won't print during successful tests unless --nocapture is specified.

Test expected exceptions in Kotlin

In Java, the programmer can specify expected exceptions for JUnit test cases like this:
#Test(expected = ArithmeticException.class)
public void omg()
{
int blackHole = 1 / 0;
}
How would I do this in Kotlin? I have tried two syntax variations, but none of them worked:
import org.junit.Test
// ...
#Test(expected = ArithmeticException) fun omg()
Please specify constructor invocation;
classifier 'ArithmeticException' does not have a companion object
#Test(expected = ArithmeticException.class) fun omg()
name expected ^
^ expected ')'
The Kotlin translation of the Java example for JUnit 4.12 is:
#Test(expected = ArithmeticException::class)
fun omg() {
val blackHole = 1 / 0
}
However, JUnit 4.13 introduced two assertThrows methods for finer-granular exception scopes:
#Test
fun omg() {
// ...
assertThrows(ArithmeticException::class.java) {
val blackHole = 1 / 0
}
// ...
}
Both assertThrows methods return the expected exception for additional assertions:
#Test
fun omg() {
// ...
val exception = assertThrows(ArithmeticException::class.java) {
val blackHole = 1 / 0
}
assertEquals("/ by zero", exception.message)
// ...
}
Kotlin has its own test helper package that can help to do this kind of unittest.
Your test can be very expressive by use assertFailWith:
#Test
fun test_arithmethic() {
assertFailsWith<ArithmeticException> {
omg()
}
}
You can use #Test(expected = ArithmeticException::class) or even better one of Kotlin's library methods like failsWith().
You can make it even shorter by using reified generics and a helper method like this:
inline fun <reified T : Throwable> failsWithX(noinline block: () -> Any) {
kotlin.test.failsWith(javaClass<T>(), block)
}
And example using the annotation:
#Test(expected = ArithmeticException::class)
fun omg() {
}
You can use Kotest for this.
In your test, you can wrap arbitrary code with a shouldThrow block:
shouldThrow<ArithmeticException> {
// code in here that you expect to throw a ArithmeticException
}
JUnit5 has kotlin support built in.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class MyTests {
#Test
fun `division by zero -- should throw ArithmeticException`() {
assertThrows<ArithmeticException> { 1 / 0 }
}
}
You can also use generics with kotlin.test package:
import kotlin.test.assertFailsWith
#Test
fun testFunction() {
assertFailsWith<MyException> {
// The code that will throw MyException
}
}
Nobody mentioned that assertFailsWith() returns the value and you can check exception attributes:
#Test
fun `my test`() {
val exception = assertFailsWith<MyException> {method()}
assertThat(exception.message, equalTo("oops!"))
}
}
This simple sample worked in the 4.13.2 version of Junit
#Test
fun testZeroDividing(){
var throwing = ThrowingRunnable { /*call your method here*/ Calculator().divide(1,0) }
assertThrows(/*define your exception here*/ IllegalArgumentException::class.java, throwing)
}
Assert extension that verifies the exception class and also if the error message match.
inline fun <reified T : Exception> assertThrows(runnable: () -> Any?, message: String?) {
try {
runnable.invoke()
} catch (e: Throwable) {
if (e is T) {
message?.let {
Assert.assertEquals(it, "${e.message}")
}
return
}
Assert.fail("expected ${T::class.qualifiedName} but caught " +
"${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")
}
for example:
assertThrows<IllegalStateException>({
throw IllegalStateException("fake error message")
}, "fake error message")
org.junit.jupiter.api.Assertions.kt
/**
* Example usage:
* ```kotlin
* val exception = assertThrows<IllegalArgumentException>("Should throw an Exception") {
* throw IllegalArgumentException("Talk to a duck")
* }
* assertEquals("Talk to a duck", exception.message)
* ```
* #see Assertions.assertThrows
*/
inline fun <reified T : Throwable> assertThrows(message: String, noinline executable: () -> Unit): T =
assertThrows({ message }, executable)
Another version of syntaxis using kluent:
#Test
fun `should throw ArithmeticException`() {
invoking {
val backHole = 1 / 0
} `should throw` ArithmeticException::class
}
Firt steps is to add (expected = YourException::class) in test annotation
#Test(expected = YourException::class)
Second step is to add this function
private fun throwException(): Boolean = throw YourException()
Finally you will have something like this:
#Test(expected = ArithmeticException::class)
fun `get query error from assets`() {
//Given
val error = "ArithmeticException"
//When
throwException()
val result = omg()
//Then
Assert.assertEquals(result, error)
}
private fun throwException(): Boolean = throw ArithmeticException()