Cannot mock a method in mockito - unit-testing

I tried mocking this function but the record variable is always null
Here is the code in the service:
val record: LockDatabaseRecord = lockDatabaseRecordService.getTransLockDbRecord(dbRecordId) {
it.lockDatabaseRecordKeys.size
}
And this is how I mocked it
#Test
fun failedEngageSessionItem() {
val dbVersion = "0x0000001"
val sessionId = UUID.randomUUID()
val dbRecordId = UUID.randomUUID()
val requestDetails = "request details"
val recordDbVersion = "1x"
val lockId = UUID.randomUUID()
val lock = mock(Lock::class.java)
val lockDatabaseRecord = mock(LockDatabaseRecord::class.java)
val lockDatabaseRecordKey = mock(LockDatabaseRecordKey::class.java)
val key = mock(Key::class.java)
`when`(lockService.getTransLock(eq(lockId), any())).thenReturn(lock)
`when`(lockDatabaseRecordService.getTransLockDbRecord(eq(dbRecordId), any())).thenReturn(lockDatabaseRecord)

From the way you call it, it seems like getTransLockDbRecord accepts two arguments (an id and a lambda that returns the size of keys). Make sure you mock it exactly like it is supposed to be called, icluding the lambda parameter.
In the bitecode, these two calls would be calls to two different methods (with overloaded signatures), you need to make sure the correct one is called.
Mockito might also have issues comparing lambdas. If mocking doesn't work I would suggest you first try it with Mockito.any() just to make sure it manages to mock it.
`when`(lockDatabaseRecordService.getTransLockDbRecord(eq(dbRecordId), any()))
.thenReturn(lockDatabaseRecord)

Related

Kotlin & MockK - mocking not working if a mocked method is called from another method

I have a problem with MockK.
I have a class:
#Service
class ItemServiceImpl(private val varPuObjectMapper: VarPuObjectMapper) : OutboundAdvicesService {
override suspend fun getItemsForWarehouse(warehouseId: String): ItemsDTO {
// do stuff
}
override suspend fun getPickingListsForWarehouse(warehouseId: String): PickingListsDTO {
val groupedOutboundAdvices = getItemsForWarehouse(warehouseId)
// do other stuff
}
}
and a test for this class:
class ItemServiceGroupingTest : FunSpec({
val warehouseId = "1"
val myObjectMapper = MyObjectMapper()
val itemService = mockk<ItemServiceImpl>()
beforeTest {
val items1 = myObjectMapper
.getObjectMapper()
.readValue(Mockups.ITEMS_1, ItemsDTO::class.java)
coEvery {
itemService.getItemsForWarehouse(warehouseId)
} returns items1
}
test("should get items for warehouse with ID 1") {
val itemsDTO = itemService.getItemsForWarehouse(warehouseId)
// assertions here
}
test("should get picking lists for warehouse with ID 1") {
val pickingLists = itemService.getPickingListsForWarehouse(warehouseId)
// assertions here
}
})
Now the first test passes successfully, but the second one fails:
no answer found for: ItemServiceImpl(#1).getPickingListsForWarehouse(1, continuation {})
io.mockk.MockKException: no answer found for: ItemServiceImpl(#1).getPickingListsForWarehouse(1, continuation {})
at app//io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:93)
From what I understand, this fails cause the getPickingListsForWarehouse method is not mocked. Is it possible to call a real method using MockK? I tried to use spyk instead of mockk, and I tried mockk with relaxed = true, but it got me nowhere...
The problem with the second test is that you are trying to call a method from a mock without specified behavior. The first test passes because you already set the value which should be returned for the method call itemService.getItemsForWarehouse(warehouseId) in this statement in beforeTest:
coEvery {
itemService.getItemsForWarehouse(warehouseId)
} returns items1
You have to do the same for getPickingListsForWarehouse or call a real method like:
every { itemService.getPickingListsForWarehouse(warehouseId) } answers { callOriginal() }
But then you have to use spyk instead of mock.
However, if you are asserting the object which you provided within the mock, you are not testing the real implementation of the method under test. You are just testing the mock, so if you change the implementation of your method this test still will be passing. beacuse it doesn't call your real object.

Getting a List from Coroutines

I try to make a method that would get a list of movies from database using coroutine and would return me that list.
But as you know coroutine returns Deferred, not a list, so I have a problem here.
suspend fun getMovieList(): List<MovieLocalModel>{
val list = viewModelScope.async {
dbRepository.getMovie().toList()
}
return list
}
How can I convert Deferred List<MovieLocalModel to List<MovieLocalModel?
Or is there a method to get a list from LiveData?
To get an object from Deffered you can use await method:
suspend fun getMovieList(): List<MovieLocalModel>{
val list = viewModelScope.async {
dbRepository.getMovie().toList()
}
return list.await()
}
If you use LiveData you can get an object using value property:
val list = livaDataObj.value
If you don't have a reason this fetch needs to specifically be done in the viewModelScope, which is likely the case since it is only fetching something (not saving something), you can simplify this by calling the function directly.
suspend fun getMovieList(): List<MovieLocalModel> =
dbRepository.getMovie().toList()

How to throw an exception upon first invocation and a regular return value on the second?

My code makes network calls. Sometimes these fail and I've implemented some retry logic. Now I wish to test this with a unit test. I'm using Mockito for this. I'm having trouble mocking this particular method call, this is my test code:
var mock = MockHttpClient();
var answers = <Future<String>>[
Future.error(Exception('boom')),
Future.value('some return value'),
];
when(mock.getStuff()).thenAnswer((_) => answers.removeAt(0));
var client = Client(inner: mock);
var result = client.someCall(); // This method call uses the `inner` object to make the `getStuff` network call.
expect(result, 'some return value');
Running this code throws an exception. And I get that because of the first returned Future! Mockito has the appropriate method call named thenThrow but I can't figure out how to combine the two.
I've figured it out. The trick is not to use a Mock but a Fake.
That looks like this:
class FakeHttpClient extends Fake implements HttpClient {
final List<Future<List<String>>> answers = [
Future.error(Exception('boom')),
Future.value('some return value'),
];
#override
Future<String> getStuff() {
return answers.removeAt(0);
}
}
Then use an instance of FakeHttpClient as an argument to the original example's Client:
var fake = FakeHttpClient();
var client = Client(inner: fake);
var result = client.someCall(); // This method call uses the `inner` object to make the `getStuff` network call.
expect(result, 'some return value');

How to create Single.just(Void)

I am writing some unit test cases for my application. I want to mock MongoClient update method, but the update returns Single<Void>.
when(mongoClient.rxUpdate(anyString(), any(JsonObject.class), any(JsonObject.class)))
.thenReturn(Single.just(Void))
Now Single.just(Void) doesn't work, what is the correct way of doing it?
--UPDATE--
So I am writing unit test for updateUserProfile method and for that I have mocked service. But the service.updateAccount method return is something I am not able to mock.
//Controller class
public void updateUserProfile(RoutingContext routingContext){
// some code
service.updateAccount(query, update)
.subscribe(r -> routingContext.response().end());
}
//Service Class
public Single<Void> updateAccount(JsonObject query, JsonObject update){
return mongoClient.rxUpdate("accounts", query, update);
}
Because the return type of mongoClient.rxUpdate is Single<Void>, I am not able to mock that part.
For now the workaround which I have figured out is:
public Single<Boolean> updateAccount(JsonObject query, JsonObject update){
return mongoClient.rxUpdate("accounts", query, update).map(_void -> true);
}
But this is just a hacky way of doing it, I want to know how can I exactly create Single<Void>
Having a method returning Single<Void> may raise some concerns, as some users have already expressed their view on this in the comments.
But if you are stuck with this and you really need to mock it (for whatever reason), there are definitely ways to create a Single<Void> instance, for example you could use the create method of the Single class:
Single<Void> singleVoid = Single.create(singleSubscriber -> {});
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleVoid);
Single<Void> result = test.updateAccount(null, null);
result.subscribe(
aVoid -> System.out.println("incoming!") // This won't be executed.
);
Please note: you won't be able to actually emmit a Single item, since Void can't be instantiated without reflection.
A trick that could eventually work in some cases is to ommit the generic type argument and emmit an Object instead, but this could lead easily to a ClassCastException. I would not recommend to use this:
Single singleObject = Single.just(new Object());
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleObject);
Single<Void> result = test.updateAccount(null, null);
// This is going to throw an exception:
// "java.base/java.lang.Object cannot be cast to java.base/java.lang.Void"
result.subscribe(
aVoid -> System.out.println("incoming:" + aVoid)
);
And of course you could use reflection as well (as already suggested by Minato Namikaze):
Constructor<Void> constructor = Void.class.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
Void instance = constructor.newInstance();
Single<Void> singleVoidMock = Single.just(instance);
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleVoidMock);
Single<Void> result = test.updateAccount(null, null);
result.subscribe(
aVoid -> System.out.println("incoming:" + aVoid) // Prints: "incoming:java.lang.Void#4fb3ee4e"
);

Testing interfaces with foq

I am trying to use Foq to testing an interface with Foq.
So far, all examples I have seen for this have been relatively simple, such as the following:
let users = [|{ID = 1; pass = true};{ID = 2; pass= false}|]
type IFoo =
abstract member Bar: int -> bool
//tests with Foq
let dataAccess =
Mock<IFoo>()
.Setup(fun x-> <# x.Bar(users.[0].ID) #>).Returns(users.[0].pass)
.Setup(fun x-> <# x.Bar(users.[1].ID) #>).Returns(users.[1].pass)
.Create()
The examples have been sourced from 'Testing with F# - Mikael Lundin'
I have also researched this through a bit of googling (this link was helpful - http://trelford.com/blog/post/Foq.aspx)
However, the real Interfaces I want to test are the following:
type IParameters =
abstract member ParameterDate : int->string->DateTime
type IDataSource =
abstract member MortParameters: IParameters
I have tried a number of different ways to test these (e.g. defining a function with a signature of int->string to be used as the input to the setup. Alternatively, having the return value as a string->DateTime and the Setup as just an integer.
My question is really the following: When testing interfaces using Foq, how can I extend the testing to interfaces with function signatures of any general length (e.g. a->b->c->d->e etc.)
Since ParameterDate a property with a function type, you could just set it up as a property that returns a lambda value. See an example of property set-up in Foq. This should be easy to modify for your case:
let instance =
Mock<System.Collections.IList>()
.Setup(fun x -> <# x.Count #>).Returns(1)
.Create()
However, I guess you would lose the ability to have a strict mock with fixed expectations on the function inputs.
To enforce only expected inputs for the function returned by the mock property you could provide a function like this:
fun i s ->
match i, s with
| 1, "" -> DateTime.Now
| _ -> failwith "Invalid mock input"
I would probably stop here, but if you're working with code where you need to verify a function was called, as opposed to just ensuring you get the correct output, you could add a helper like this:
type Verifiable<'a, 'b> (f : 'a -> 'b) =
let called = ref false
member this.Func x =
called := true
f x
member this.Verify() =
if not called.Value then failwith "Mock function was not called"
And here's how you would use it:
let parameterDateMock =
fun i s ->
match i, s with
| 1, "" -> DateTime.Now
| _ -> failwith "Unexpected mock input"
|> Verifiable
let parameters =
{ new IParameters with member this.ParameterDate i s = parameterDateMock.Func i s }
parameters.ParameterDate 1 ""
parameterDateMock.Verify()
Caveat: This only verifies the function was called with at least one parameter. It may have returned another function by currying and not actually run the code in the mock function body. To get around that you'd need a variation of the Verifiable class for every function arity and use the right one in each case.