Unit test cases for Callbacks using Junit - java

I'm trying to write some unit-test cases for a class with functions that has callbacks as arguments (Please see code below)
class NetworkApi(private val url: String) {
fun getToken(listener: (String) -> Unit) {
Thread(Runnable {
Thread.sleep(2000)
if (TextUtils.isEmpty(url)) listener("")
else listener("Dummy token")
}).start()
}
}
and the unit test case is
#RunWith(AndroidJUnit4::class)
class NetworkApiTest {
var networkApi: NetworkApi? = null
#Test
fun testEmptyToken() {
networkApi = NetworkApi("")
networkApi?.getToken {
Assert.assertThat(it, isEmptyOrNullString())
}
}
}
And whenever I run this test case, I do get success all the time, no matter what values I send. I know that I'm not doing exact way.
Can someone please help me writing unit test cases for classes in JUnit.

The problem is that the test finishes before the callback is invoked and the assert is in the wrong thread.
You have to copy the result from the callback back to the main thread. Use a CompletableFuture.
If you like to fail the test after a period of time you can use the get method with a timeout value:
#RunWith(AndroidJUnit4::class)
class NetworkApiTest {
var networkApi: NetworkApi? = null
#Test
fun testEmptyToken() {
val future = CompletableFuture<String>()
networkApi = NetworkApi("")
networkApi?.getToken {
future.complete(it)
}
val result = future.get(3,TimeUnit.SECONDS)
Assert.assertThat(it, isEmptyOrNullString())
}
}

Related

how to mock a method call inside another method in same class

I am trying to test a method methodB (as shown in the code below). I want to return directly from methodA without going into the actual code of methodA.
I have used when/thenReturn and doReturn/when but didn't get any success. The test case goes into the real code of methodA. Also tried using spy with Class A instance but didn't get any success.
Actual Class
class A{
fun methodA(String a): String{
// do something
throw new Exception("An error occured");
}
fun methodB(String b): String{
try{
methodA("test")
} catch (e: Exception){
println(e.message());
}
}
}
Test Class
class ATest{
private lateinit var a: A
#Before
fun setUp() {
a= A() // I am initializing services in real scenario
}
#Test
fun `when methodB is not valid then throw Exception`(){
val aMock = mock(A)
Mockito.when(aMock.methodA("test") )
.thenThrow(UserException.INVALID_REQUEST())
// When
val exception: Exception = Assert.assertThrows(
UserException::class.java
) {
a.methodB("test b")
}
val expectedMessage = "INVALID"
val actualMessage = exception.message
// Then
Assert.assertTrue(actualMessage!!.contains(expectedMessage))
}
}
Can anyone please help?
val a = A()
val aSpy = Mockito.spy(a)
Mockito.when(aSpy.methodA(ArgumentMatchers.any())).thenThrow(UserException.INVALID_REQUEST())
you need to get a real instance of class A and then wrap it inside a programmable wrapper which is a spy.
The param matcher of the when statement is the 2nd point of failure since "Test" is not the same instance then the "test b" string. So you dont need to verify some parameter so skip it.
If you want to verify the used parameter you can use a captor instead of ANY().

Spring Webflux Mockito - mock the response of a Webclient call

Small question regarding how to "force" or "mock" the response of a Webclient http call during unit test phase please.
I have a very straightforward method which does:
public String question() {
String result = getWebClient().mutate().baseUrl(someUrlVariable).build().post().uri("/test").retrieve().bodyToMono(String).block();
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
As you can see, the complex part of this method is the Webclient call. It has (in this example) 7 .method() like .mutate(), .post(). etc...
In my use case, I am not that interested testing this Webclient, at all.
What I would like to have with Mockito, is somehow the equivalent of:
public String question() {
// it is just unit test. Mockito, please just return me the string I tell you to return please. Don't even execute this next line if possible, just return me this dummy response
String result = the-thing-I-tell-mockito-to-return;
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
So far, I tried Mockito doNothing(), or Mockito.when(getWebclient()... ) of the entire line plus .thenReturn, but no luck.
How to achieve such please?
I would like to avoid those copy/pasting of when()
Well you have designed your code so that the only way to test it is by copy pasting of when.
So how have you designed it? well you have mixed API-code with logic which is something you should not do. The first thing you need to think about when writing tests is "What is it i want to test?" and the answer is usually Business logic.
If we look at your code:
public String question() {
// This is api code, we dont want to test this,
// spring has already tested this for us.
String result = getWebClient()
.mutate()
.baseUrl(someUrlVariable)
.build()
.post()
.uri("/test")
.retrieve()
.bodyToMono(String)
.block();
// This is logic, this is want we want to test
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
}
When we design an application, we divide it into layers, usually a front facing api (RestController), then the business logic in the middle (Controllers) and lastly different resources that call other apis (repositories, resources etc.)
So when it comes to your application i would redesign it, split up the api and the logic:
#Bean
#Qualifier("questionsClient")
public WebClient webclient(WebClient.Builder webClient) {
return webClient.baseUrl("https://foobar.com")
.build();
}
// This class responsibility is to fetch, and do basic validation. Ensure
// That whatever is returned from its functions is a concrete value.
// Here you should handle things like basic validation and null.
#Controller
public class QuestionResource {
private final WebClient webClient;
public QuestionResource(#Qualifier("questionsClient") WebClient webClient) {
this.webClient = webClient;
}
public String get(String path) {
return webClient.post()
.uri(path)
.retrieve()
.bodyToMono(String)
.block();
}
}
// In this class we make business decisions on the values we have.
// If we get a "Foo" we do this. If we get a "Bar" we do this.
#Controller
public class QuestionHandler {
private final QuestionResource questionResource;
public QuestionResource(QuestionResource questionResource) {
this.questionResource = questionResource;
}
public String get() {
final String result = questionResource.get("/test");
// also i dont see how the response can be null.
// Null should never be considered a value and should not be let into the logic.
// Because imho. its a bomb. Anything that touches null will explode (NullPointerException).
// Null should be handled in the layer before.
if (result == null) {
return doSomething1();
}
if (result.equals("")) {
return doSomething2();
}
if (result.equals("foo")) {
return doSomething3();
}
}
}
Then in your test:
#Test
public void shouldDoSomething() {
final QuestionResource questionResourceMock = mock(QuestionResource.class);
when(questionResourceMock.get("/test")).thenReturn("");
final QuestionHandler questionHandler = new QuestionHandler(questionResourceMock);
final String something = questionHandler.get();
// ...
// assert etc. etc.
}
Also, i suggest you don't mutate webclients, create one webclient for each api because it gets messy fast.
This is written without an IDE, so there might be compile errors etc. etc.
You have to first ensure that getWebclient() returns a mock. Based on your existing code example I can't tell if that's coming for a different class or is a private method (it might make sense to inject the WebClient or WebClient.Builder via the constructor).
Next, you have to mock the whole method chain with Mockito. This includes almost copy/pasting your entire implementation:
when(webClient.mutate()).thenReturn(webClient);
when(webClient.baseUrl(yourUrl)).thenReturn(...);
// etc.
Mockito can return deep stubs (check the documentation and search for RETURN_DEEP_STUBS) that could simplify this stubbing setup.
However, A better solution would be to spawn a local HTTP server for your WebClient test and mock the HTTP responses. This involves less Mockito ceremony and also allows testing error scenarios (different HTTP responses, slow responses, etc.),

Unit test for doOnError method when error comes from zip method of Mono

I have following code:
public void process(AnotherObject anotherObject) {
Mono.zipDelayError(
this.zipService.getObject(id1),
this.zipService.getObject(id2),
this.zipService.getObject(id3),
this.zipService.getObject(id4))
.map(objects -> {
return Tuples.of(anotherObject, objects.getT1(), objects.getT2());
}).onErrorMap(Function.identity())
.doOnError(throwable -> {
Error error = create(throwable);
this.errorService.procesarError(error);
}).suscribe(objects ->
this.dbservice.save(objects.getT2())
.doOnError(throwable -> {
Error error = create(throwable);
this.errorService.procesarError(error);
})
.subscribe(dbObject -> {
log.info("Objeto registrado en BD {}", dbObject);
})
)
}
When I make the unit test for that method:
#Test
public void shouldSaveDocumentoSendAnErrorEventWhenAnUnexpectedErrorHappens() {
doReturn(Mono.error(Throwable::new)).when(this.zipService)
.getObject(anyString());
this.service.process(anotherObject);
verify(this.errorService, times(1)).processError(anyObject());
}
This unit test always fails. But when I debugged in real time and simulate an error, the processError method is invoked (the flow code enter to doOnError method) so I suppose that I am making the unit test in a wrong way.
So, what it would the correct way to write the unit test?
Thanks in advance.
EDIT
I tried to simplify my method and forgot it the last part. At the final of method I make a suscribe(). In this part I invoke a service responsible of DB transactions. Well...the unit test does not work with the current version of my method either

Automatically post test results in JIRA Adaptavist Tool from rest api test cases run on eclipse

I am looking for an idea to update my automation test cases in JIRA Adaptavist Test Management Tool. The test cases are written in Java & TestNg and are running on a VM. Please suggest what is a good method to update test results(Pass Or Fail) automatically in JIRA Adaptavist Test Management Tool. Currently, we are manually updating results in the tool.
Problem with using Zephyr and XRay or any other thrid-party COTS tool is that because of the cumbersome and delayed approval processes.
I appreciate your time, help and support.
Our Android test team developed the following code and we don't know how to achieve as they did.
Here is the android team's code :
Open class rTestCaseHelper {
#rule
#JvmField
val grantWriteStorageAccess: GrantPermissionRule = GrantPermissionRule.grant(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
#rule
#JvmField
val reportRule :TestWatcher = Object : TestWatcher() {
var startTime = 0
override fun starting(description: Description?) {
startTime = System.currentTimeMillis().toInt()
if (sharedPreference.getTestCount() == 0) {
testManager.getTestIdAndStoreToSharedPreference()
testManager.createTestCycle()
}
}
override fun succeeded(description: Description?) {
if(description != null) {
val executionTime = System.currentTimeMillis().toInt() - startTime
testManager.generateExecutionReport(description.methodName, result: "Pass", executionTime)
}
}
override fun failed(e: Throwable?, description: Description?) {
super.failed(e.description)
if(description != null) {
val executionTime = System.currentTimeMillis().toInt() - startTime
testManager.generateExecutionReport(description.methodName, result: "Fail", executionTime, e)
}
}
override fun finished(description: Description?) {
sharedPreference.setTestCount(sharedPreference.getTestCount() -1)
//Post artfact report link to test cycle (TODO)
testManager.postWebLinkToTestCycle()
rSharedPreference.preference.edit().clear()
Log.d(tag: "QAA", msg: "Automation suite finished and sharedPreference data is cleared!")
}
}
}
fun initializeSDK(activeProtection: Boolean = false): Boolean {
rSdkEnvironment.initializeSDK(activeProtection, InstrumentationRegistry.getInstrumentation().targetContext)
return rSdk.isInitialized()
}
Not sure what exactly are you looking for.
Here's one way of doing this:
Create a new custom annotation which can be used to refer to the Jira ID that represents a particular test case.
Annotate the #Test method with the new annotation so that we tie down a test method to its JIRA ID.
Now build a TestNG listener by implementing ITestNGListener and within its onTestFailure() retrieve the Jira ID by retrieving the custom annotation via the ITestResult.getTestMethod().getConstructorOrMethod().getMethod() call, and then fire a RESTful call to your Jira instance to update the test results.
"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
testManager.postWebLinkToTestCycle()
rSharedPreference.preference.edit().clear()
Log.d(tag: "QAA", msg: "Automation suite finished and sharedPreference data is cleared!")
}
}
}
fun initializeSDK(activeProtection: Boolean = false): Boolean {
rSdkEnvironment.initializeSDK(activeProtection, InstrumentationRegistry.getInstrumentation().targetContext)
return rSdk.isInitialized()
}

Mockito's argThat returning null when in Kotlin

Given the following class (written in kotlin):
class Target {
fun <R> target(filter: String, mapper: (String) -> R): R = mapper(filter)
}
I'm able to test in java, the test code:
#Test
public void testInJava() {
Target mockTarget = Mockito.mock(Target.class);
Mockito.when(mockTarget.target(
argThat(it -> true),
Mockito.argThat(it -> true)
)).thenReturn(100);
assert mockTarget.target("Hello World", it -> 1) == 100;
}
The java test pass as expected, but the same test is written in kotlin:
#Test
fun test() {
val mockTarget = Mockito.mock(Target::class.java)
Mockito.`when`(mockTarget.target(
Mockito.argThat<String> { true },
mapper = Mockito.argThat<Function1<String, Int>>({ true }))
).thenReturn(100)
assert(mockTarget.target("Hello World") { 1 } == 100)
}
The kotlin version I receive the following exception:
java.lang.IllegalStateException: Mockito.argThat<String> { true } must not be null
Why is it happening and how can I test that using kotlin?
I also faced the same problem.
And finally, I found argThat() will return null, and normally the argument in the function in kotlin, does not accept null type.
The source code of argThat from ArgumentMatchers.java
public static <T> T argThat(ArgumentMatcher<T> matcher) {
reportMatcher(matcher);
return null;
}
You can see that it return null. So when we mock the function, it will throw IllegalStateException, because argThat returns null and argument can't be null.
It mean that if your function is:
fun doSomething(arg1: String): Int {
// do something
}
When you mock it like that:
Mockito.`when`(
doSomething(Mockito.argThat<String> { true })
).thenReturn(100)
It will throw IllegalStateException
So you should change your function like that:
fun doSomething(arg1: String?): Int {
// do something
}
Change the "String" to "String?", make it accept null type.
My solution is to define the argument with class? so that it can accept null, but I don't know if it is a great solution
In 2022, Mockito-Kotlin officially solves the problem.
The fix is very simple: Just import the argThat/eq/... from the mockito-kotlin package, instead of the mockito package, and everything is done!
Related: https://github.com/mockito/mockito-kotlin/wiki/Mocking-and-verifying
As of this writing, mockito-kotlin hasn't been updated for more than a year. As with all of these libraries, there's always a constant need for keeping them up-to-date, and I didn't want to get stuck with an unmaintained library.
So I came up with another way to solve the null issue with argThat without using any other libraries.
Say we've an interface UuidRepository as follows:
interface UuidRepository {
suspend fun Entity save(entity: Entity): Entity
}
class Entity has two properties, userId: String and uuid: String.
The following code fails:
Mockito.verify(uuidRepository).save(argThat { it.userId == someValue && it.uuid == "test" })
with the error:
argThat { it.userId == someValue && it.uuid == "test" } must not be null
To solve this, we get all the invocation on the mock and then verify the ones we want:
val invocations = Mockito.mockingDetails(uuidRepository).invocations
.filter { setOf("findById", "save").contains(it.method.name) }
.map { it.method.name to it.arguments }
.toMap()
assertThat(invocations).containsKey("save")
val first = invocations["save"]?.first()
assertThat(first).isNotNull
val entity = first as Entity
assertThat(entity.userId).isEqualTo(someValue)
assertThat(entity.uuid).isEqualTo("test")

Categories