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()
Related
This is my ViewModel and I want to throw an exception so to catch branch is executed.
class PopularSearchViewModel #Inject constructor(
private val popularSearchUseCase: LoadPopularSearchUseCase,
private val coroutineDispatcherProvider: CoroutineDispatcherProvider
) :
ViewModel() {
private val listOfCatalogProductMutableStateFlow = MutableStateFlow<List<CatalogProduct>>(emptyList())
val listOfCatalogProductStateFlow = listOfCatalogProductMutableStateFlow.asStateFlow()
fun getPopularProducts() {
viewModelScope.launch(coroutineDispatcherProvider.io()) {
try {
listOfCatalogProductMutableStateFlow.value = popularSearchUseCase.execute()
} catch (exception: Exception) {
Timber.e(exception, "popular ${exception.localizedMessage}")
}
}
}
}
This is the actual unit test. In the mock return I am passing an exception. But when running the test I don't think the exception is thrown so the catch block is never executed.
#Test(expected = Exception::class)
fun `should not get popular search products if exception`() {
runBlockingTest {
// Arrange
val listOfEmittedResult = mutableListOf<List<CatalogProduct>>()
val job = launch {
popularSearchViewModel.listOfCatalogProductStateFlow.toList(listOfEmittedResult)
}
whenever(loadPopularSearchUseCase.execute()).thenThrow(Exception("Something bad happened"))
// Act
popularSearchViewModel.getPopularProducts()
// Assert
assertThat(listOfEmittedResult).isEmpty()
verify(loadPopularSearchUseCase).execute()
job.cancel()
}
}
using mockk 1.9.3
having a function to be verified
class EventLogger private constructor()
fun logUserEvent(eventName: String?, eventParamMap: MutableMap<String, String>?) {
......
internaLogEventImpl(eventName, eventParamMap)
}
internal fun internaLogEventImpl(eventName: String?, customParams: MutableMap<String, String>?) {
......
}
companion object {
#Volatile
private var sEventLoggerSingleton: EventLogger? = null
#JvmStatic
val instance: EventLogger
get() {
if (sEventLoggerSingleton == null) {
sEventLoggerSingleton = EventLogger()
}
return sEventLoggerSingleton!!
}
}
got compiler error at every {eventLogger.internaLogEventImpl(any(), mapSlot)}
Type mismatch.
Required: MutableMap<String, String>?
Found: CapturingSlot<MutableMap<String, String>>
when trying this below :
class TestK {
lateinit var eventLogger: EventLogger
lateinit var application: Application
val mapSlot = slot<MutableMap<String, String>>()
#Before
fun setUp() {
application = ApplicationProvider.getApplicationContext<Application>()
eventLogger = mockk.spyk(EventLogger.instance)
ReflectionHelpers.setStaticField(EventLogger::class.java, "sEventLoggerSingleton", eventLogger)
}
#After
fun cleanUp() {
ReflectionHelpers.setStaticField(EventLogger::class.java, "sEventLoggerSingleton", null)
}
#Test
fun logNotificationStatusChange_with_enabled_WhenCalled_ShouldLog() {
val testMap = hashMapOf("action" to "open")
every {eventLogger.internaLogEventImpl(any(), mapSlot)} answers {
println(mapSlot.captured)
assert(mapSlot.captured["action"] == "open")
}
eventLogger.logUserEvent("test_event", testMap)
}
}
You need to use capture (see Mockk's Capturing section).
So for your case capture(mapSlot) should work.
eventLogger.internaLogEventImpl(any(), capture(mapSlot))
Before trying to mock on complex code, It would be better to learn on easier example.
Here is a working example to mock a private call on an object with mockk.
object MyLogger {
fun logUserEvent(event: String?, map: MutableMap<String, String>?) {
// turns the event string into an uppercase string.
internaLogEventImpl(event?.toUpperCase(), map)
}
private fun internaLogEventImpl(event: String?, map: MutableMap<String, String>?): Unit =
throw Exception("real implementation")
}
How to test and mock the internal function so we don't throw the exception.
#Test
fun `test logger internal`() {
val expectedMap = mutableMapOf("a" to "b")
val expectedEvent = "EVENT"
val mock = spyk(MyLogger, recordPrivateCalls = true)
justRun { mock["internaLogEventImpl"](expectedEvent, expectedMap) }
// or justRun { mock["internaLogEventImpl"](any<String>(), any<MutableMap<String, String>>()) }
mock.logUserEvent("event", expectedMap)
verify { mock["internaLogEventImpl"](expectedEvent, expectedMap) }
}
Here logUserEvent calls the real implementation and internaLogEventImpl calls the mock implementation.
if justRun { mock["internaLogEventImpl"](expectedEvent, expectedMap) } is not called (or wrong because the argument doesn't match), the real implementation will be call. Here it will throw Exception("real implementation").
Please try and modify values to check different behaviors.
I am trying to mock a method (getQualityControlCheckDataForUplift(ArgumentMatchers.any(), ArgumentMatchers.anyFloat())) to return qcchecks which I have defined in my test case but I get an error.
Any suggestions on where I am doing it wrong please
org.mockito.exceptions.base.MockitoException: Unable to create mock instance of type 'QualityControlChecksDataProvider'
QualityControlChecksDataProvider
class QualityControlChecksDataProvider #Inject constructor(
private val offlineDataStorage: OfflineDataStorage,
private val app: App
) {
private val mapOfQCChecksForMilestone = LinkedHashMap<String, ArrayList<QualityControlCheck>?>()
fun getQualityControlCheckDataForUplift(qualityControlMilestone: QualityControlMilestone, uplift: Float):
ArrayList<QualityControlCheck>? {
val qcChecksForUplift: ArrayList<QualityControlCheck>? = ArrayList()
val qcChecksForMilestone = mapOfQCChecksForMilestone[qualityControlMilestone.milestoneText]
qcChecksForMilestone?.forEach {
if (it.uplift == uplift) qcChecksForUplift?.add(it)
}
return qcChecksForUplift
}
}
In my unit test this is how I am trying to mock
#Test
fun `upliftedVolumeUpdated abcd` () {
val qualityControlCheckDataProvider = spy(QualityControlChecksDataProvider::class.java)
every(qualityControlCheckDataProvider.getQualityControlCheckDataForUplift(ArgumentMatchers.any(), ArgumentMatchers.anyFloat())).thenReturn(qualityControlChecks)
}
I don't think a spy is what you want here I think you want a mock. Give this a try
#Test
fun `upliftedVolumeUpdated abcd` () {
val qualityControlDataProviderMock = mock<QualityControlChecksDataProvider> {
on { getQualityControlCheckDataForUplift(any(), any()) } doReturn qualityControlChecks
}
}
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)
}
Currently, whenever I need to fail a test in response to an exception thrown in another thread, I write something like this:
package com.example;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import static java.util.Arrays.asList;
import static java.util.Collections.synchronizedList;
import static org.testng.Assert.fail;
public final class T {
#Test
public void testFailureFromLambda() throws Throwable {
final List<Throwable> errors = synchronizedList(new ArrayList<>());
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
throw new Exception("Error " + s);
} catch (final Throwable t) {
errors.add(t);
}
});
if (!errors.isEmpty()) {
errors.forEach(Throwable::printStackTrace);
final Throwable firstError = errors.iterator().next();
fail(firstError.getMessage(), firstError);
}
}
}
A synchronized list may be replaced with an AtomicReference<Throwable>, but in general the code remains pretty much the same.
Is there any standard (and less verbose) way of doing the same using any of test frameworks available in Java (TestNG, JUnit, Hamcrest, AssertJ, etc.)?
By default TestNG fails a test method when an exception is thrown from it. I believe the same thing happens with JUnit as well, wherein it marks a test as errored, if it throws an unexpected exception.
If you are to be dealing with Streams, then you would need to wrap it up within a RuntimeException variant, so that Java doesn't complain. TestNG would automatically fail the test.
Here's a sample :
#Test
public void testFailureFromLambdaRefactored() {
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
if (s.equals("2")) {
throw new Exception("Error " + s);
}
} catch (final Throwable t) {
throw new RuntimeException(t);
}
});
}
This was for scenarios that involve lambdas and streams. In general if you would like to know about an exception that happens in a new thread spun off from a #Test method, then you would need to use ExecutorService.
Here's a sample :
#Test
public void testFailureInAnotherThread() throws InterruptedException, ExecutionException {
List<String> list = asList("0", "1", "2");
ExecutorService service = Executors.newFixedThreadPool(2);
List<Future<Void>> futures = service.invokeAll(Arrays.asList(new Worker(list)));
for (Future future : futures) {
future.get();
}
}
public static class Worker implements Callable<Void> {
private List<String> list;
public Worker(List<String> list) {
this.list = list;
}
#Override
public Void call() throws Exception {
for (String s : list) {
if (s.equals("2")) {
throw new Exception("Error " + s);
}
}
return null;
}
}