mockk every {}.throws() Exception fails test - kotlin

I need to verify that a certain call is not made, when a previous method call throws an Exception.
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
sut.live()
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
Problem with this code, it fails because of the Exception thrown and not because of the failed verification.

I understand that your WHEN block will always throw an exception.
In that case you have multiple options from my point of view:
Simple plain Kotlin. Wrap the WHEN block with a try-catch block, e.g. like this:
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
var exceptionThrown: Boolean = false
try {
sut.live()
} catch(exception: NotHungryException) {
// Maybe put some assertions on the exception here.
exceptionThrown = true
}
assertTrue(exceptionThrown)
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
For a bit nicer code, you can use JUnit5 API's Assertions. assertThrows will expect an exception being thrown by a specific piece of code. It will fail the test, if no exception is thrown. Also it will return the thrown exception, for you to inspect it.
import org.junit.jupiter.api.Assertions
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
val exception = Assertions.assertThrows(NotHungryException::class.java) { sut.live() }
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
If you're using Kotest you can use the shouldThrow assertion. Which also allows you to retrieve the thrown exception and validate its type.
import io.kotest.assertions.throwables.shouldThrow
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
val exception = shouldThrow<NotHungryException> { sut.live() }
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }

I had similar issue and found that my method is not surrounded by try catch. This mean the method will always throw exception.
Test
The unit test to verify the result when the following method is called while stubbing it with predefine Exception
#Test
fun returnSearchError() {
every { searchService.search(query) }.throws(BadSearchException())
val result = searchRepository.search(query)
assertEquals(SearchStates.SearchError, result)
}
Faulty code
fun search(query: String): SearchStates {
val result = searchService.search(query) // No try catch for the thrown exception
return try {
SearchStates.MatchingResult(result)
} catch (badSearchException: BadSearchException) {
SearchStates.SearchError
}
}
Refactored it to
fun search(query: String): SearchStates {
return try {
val result = searchService.search(query)
SearchStates.MatchingResult(result)
} catch (badSearchException: BadSearchException) {
SearchStates.SearchError
}
}

Related

How to iterate over HTTP response code in Kotlin

I am using import java.net.http.HttpClient
and my code is as follows:
try {
val response = httpClient.send(httpRequest, BodyHandlers.ofString())
...
when (response.statusCode()) {
200 -> {
result = decodedResponse
} else -> {
val errorResponse = Json.decodeFromString<ErrorObject>(response.body())
throw handleCustomError(errorResponse.error, errorResponse.error_description)
}
}
return result
} catch (ex: Exception) {
throw Exception("Service is unavailable!")
}
my handleCustomError iterates over the different types of status codes 401,403,404 etc and throws user friendly exception. But I dont think that code is ever reached. Instead I see the generic exception thrown by the catch block.
How can I make sure to iterate over the different status code?

Getting a warning when use objectmapper in flux inappropriate blocking method call in java reactor

i am new to reactor, i tried to create a flux from Iterable. then i want to convert my object into string by using object mapper. then the ide warns a message like this in this part of the code new ObjectMapper().writeValueAsString(event). the message Inappropriate blocking method call. there is no compile error. could you suggest a solution.
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
try {
return Mono.just(new ObjectMapper().writeValueAsString(event));
} catch (JsonProcessingException e) {
return Mono.error(e);
}
})
.subscribe(jsonStrin -> {
System.out.println("jsonStrin = " + jsonStrin);
});
I will give you an answer, but I don't pretty sure this is what you want. it seems like block the thread. so then you can't get the exact benefits of reactive if you block the thread. that's why the IDE warns you. you can create the mono with monoSink. like below.
AtomicReference<ObjectMapper> objectMapper = new AtomicReference<>(new ObjectMapper());
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
return Mono.create(monoSink -> {
try {
monoSink.success(objectMapper .writeValueAsString(event));
} catch (JsonProcessingException e) {
monoSink.error(e);
}
});
})
.cast(String.class) // this cast will help you to axact data type that you want to continue the pipeline
.subscribe(jsonString -> {
System.out.println("jsonString = " + jsonString);
});
please try out this method and check that error will be gone.
it doesn't matter if objectMapper is be a normal java object as you did. (if you don't change). it is not necessary for your case.
You need to do it like this:
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
try {
return Mono.just(new ObjectMapper().writeValueAsString(event));
} catch (JsonProcessingException e) {
return Mono.error(e);
}
})
.subscribe(jsonStrin -> {
System.out.println("jsonStrin = " + jsonStrin);
});

Catch Exception A Throw Exception B - Coverage Junit5

Iam catch FeignException, but im throws FeingClientException(customized exception).
Sonar says that this FeingClientExceptionis not coveraged by junit test.
Any idea to cover it.
My exception is like this:
public class FeignClientException extends RuntimeException {
public FeignClientException(FeignException ex, ErrorMessageEnum errorMessageEnum, ContextExceptionEnum contextExceptionEnum, String cpf) {
throwException(ex, contextExceptionEnum, errorMessageEnum, cpf);
}
private void throwException(FeignException ex, ContextExceptionEnum contextExceptionEnum, ErrorMessageEnum errorMessageEnum, String cpf) {
if (ex.status() == HttpStatus.NOT_FOUND.value() || ex instanceof FeignException.NotFound) {
throw new FeignClientNotFoundException(
errorMessageEnum != null ? errorMessageEnum.msgUserFormatted(cpf) : ErrorMessageEnum.REGISTRO_NAO_ENCONTRADO.msgUserFormatted(cpf),
errorMessageEnum != null ? errorMessageEnum.msgDeveloper() : ErrorMessageEnum.REGISTRO_NAO_ENCONTRADO.msgDeveloper(),
String.valueOf(HttpStatus.NOT_FOUND.value()),
contextExceptionEnum.value());
}
if (ex.status() == HttpStatus.SERVICE_UNAVAILABLE.value() || ex instanceof RetryableException) {
throw new ServicoForaDoArException(
ErrorMessageEnum.SERVICO_FORA_AR.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_FORA_AR.msgDeveloper(),
String.valueOf(HttpStatus.SERVICE_UNAVAILABLE.value()),
contextExceptionEnum.value());
}
if (ex.status() == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
throw new FeignClientInternalServerErrorException(
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgDeveloper(),
String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()),
contextExceptionEnum.value());
}
throw new FeignBadGatewayException (
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgDeveloper(),
String.valueOf(HttpStatus.BAD_GATEWAY.value()),
contextExceptionEnum.value());
}
}
My test is:
void test2() {
doThrow(FeignException.NotFound.class).when(preAprovadoClient).consultaPreAprovadoCDC(Mockito.anyString(), Mockito.anyString());
assertThrows(FeignClientException.FeignClientNotFoundException.class, () ->{
PreAprovado preAprovado = preAprovadoGateway.consultarPreAprovadoCDC("01234567890");
});
}
I think you're mocking the wrong method. You're calling the method consultarPreAprovadoCDC with one parameter, but you have mocked it with two String parameters, maybe that is causing this confusion.
Try to either changing the call to use two String parameters or mock the method with only one anyString() as parameter.

Failure failing in CATCH

I'm probably overlooking something simple, but I do not expect the below code to fail. It is behaving as if I wrote die instead of fail in the catch block.
The Failure does not get properly handled and the code dies.
sub foo()
{
try {
say 1 / 0;
CATCH { default { fail "FAIL" } }
}
return True;
}
with foo() {
say "done";
}
else
{
say "handled {.exception.message}"
}
Output:
FAIL
in block at d:\tmp\x.pl line 5
in any at d:\tmp\x.pl line 5
in sub foo at d:\tmp\x.pl line 4
in block <unit> at d:\tmp\x.pl line 11
To bring home to later readers the full force of what Yoda said in their comment, the simplest solution is to unlearn the notion that you have to try in order to CATCH. You don't:
sub foo()
{
say 1 / 0;
CATCH { default { fail "FAIL" } }
return True;
}
with foo() {
say "done";
}
else
{
say "handled {.exception.message}"
}
correctly displays:
handled FAIL
According to the Failure documentation this seems to be the defined behavior.
Sink (void) context causes a Failure to throw, i.e. turn into a normal exception. The use fatal pragma causes this to happen in all contexts within the pragma's scope. Inside try blocks, use fatal is automatically set, and you can disable it with no fatal.
You can try to use the no fatal pragma.
sub foo() {
try {
no fatal;
say 1 / 0;
CATCH { default { fail "FAIL" } }
}
}
unless foo() {
say "handled"
}

Catch exception in Ktor-locations if non valid route parameter

I'm new in kotlin world. So I have some problem. I'm using ktor framework and try to use ktor-locations (https://ktor.io/servers/features/locations.html#route-classes)
And as example
#Location("/show/{id}")
data class Show(val id: Int)
routing {
get<Show> { show ->
call.respondText(show.id)
}
}
Everything is good, when I try to get /show/1
But if route will be /show/test there is NumberFormatException, cause DefaultConversionService try to convert id to Int and can't do it.
So my question is, how can I catch this exception and return Json with some error data. For example, if not using locations I can do smt like this
routing {
get("/{id}") {
val id = call.parameters["id"]!!.toIntOrNull()
call.respond(when (id) {
null -> JsonResponse.failure(HttpStatusCode.BadRequest.value, "wrong id parameter")
else -> JsonResponse.success(id)
})
}
}
Thx for help!
You can do a simple try-catch in order to catch the parsing exception which is thrown when a string can not be converted to an integer.
routing {
get("/{id}") {
val id = try {
call.parameters["id"]?.toInt()
} catch (e : NumberFormatException) {
null
}
call.respond(when (id) {
null -> HttpStatusCode.BadRequest
else -> "The value of the id is $id"
})
}
}
Other way of handling exception is to use StatusPages module:
install(StatusPages) {
// catch NumberFormatException and send back HTTP code 400
exception<NumberFormatException> { cause ->
call.respond(HttpStatusCode.BadRequest)
}
}
This should work with using Location feature. Please note that Location is experimental above ktor version 1.0.