Mocking an extension function with Mockito - spring-boot

How can I test an extension function with Mockito? It doesn't seem to work nicely.
This is my extension function
fun <T> CrudRepository<T, String>.findOneById(id: String): T? {
val o = findById(id)
return if (o.isPresent) o.get() else null
}
And this is what I'm trying to test
#Test
fun getIslandById() {
//given
BDDMockito.given(islandRepository.findOneById("islandId1"))
.willReturn(IslandEntity(tileList, "1", "islandId1")) //findOneById is my extension function
//when
val island = islandService.getIslandById("islandId1")
//then
Assertions.assertThat(island?.id).isEqualTo("islandId1")
}
But the preceeding test throws the following error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
IslandEntity cannot be returned by findById()
findById() should return Optional
Any ideas?

Instance extension functions can be mocked like this with a little help of mockito-kotlin:
data class Bar(thing: Int)
class Foo {
fun Bar.bla(anotherThing: Int): Int { ... }
}
val bar = Bar(thing = 1)
val foo = mock<Foo>()
with(foo) {
whenever(any<Bar>().bla(any()).doReturn(3)
}
verify(foo).apply {
bar.bla(anotherThing = 2)
}

As I said in a comment above, in the bytecode extension functions are nothing more than static functions which accept receiver as a first argument. Therefore you can't mock an extension function with a Mockito since it is not able to mock static functions.
What you can do, in case that findById(id) is implemented by Repository and not another extension function, is next:
Mock return value of findById(id) instead.
Take a look at the sample code below:
#Test
fun getIslandById() {
//given
BDDMockito.given(islandRepository.findById("islandId1"))
.willReturn(Optional.of(IslandEntity(tileList, "1", "islandId1"))) //mock findById function
//when
val island = islandService.getIslandById("islandId1")
//then
Assertions.assertThat(island?.id).isEqualTo("islandId1")
}
This way you are indirectly mocking your extension by providing it a mocked value that you want it to operate on.
Note: Error that you posted above says that your findById(id) should return an optional. So, wrap your return value of findById(id) function with an optional by calling Optional.of(result).

Related

call private method (PowerMockito Test)

First a save() method is executed which passes the test until it reaches a condition, where if it is true it calls the saveBankAccountAndRole() method and if it is false it sends a Mono.error(new Exception("...").
The sizeAccounts(String customerId) method does pass the test.
In the saveBankAccountAndRole(BankAccountDto bnkDto) method, after executing the sizeAccounts() method, the test does not pass, what am I missing?
public Flux<BankAccountDto> findAccountsByCustomerId(String customerId) {
return mongoRepository
.findAll()
.filter(ba ->
ba.getCustomerId().equals(customerId))
.map(AppUtils::entityToDto);
}
private Mono<Long> sizeAccounts(String customerId){
return findAccountsByCustomerId(customerId)
.count();
}
private Mono<BankAccountDto> saveBankAccountAndRole(BankAccountDto bnkDto) {
return sizeAccounts(bnkDto.getCustomerId())
.flatMap(number -> {
bnkDto.setOpeningOrder(number + 1);
return mongoRepository.save(AppUtils.dtoToEntity(bnkDto))
.switchIfEmpty(Mono.error(new Exception("Problem saving the bank account")))
.zipWhen(bnk -> {
var customerRoleDto = new CustomerRoleDto();
customerRoleDto.setBankAccountId(bnk.getBankAccountId());
customerRoleDto.setCustomerId(bnkDto.getCustomerId());
customerRoleDto.setRoleType(bnkDto.getRoleType());
return webClientRoleHelper.saveCustomerRole(customerRoleDto);
})
.switchIfEmpty(Mono.error(new Exception("Problem saving roles")))
.map(tuple -> AppUtils.entityToDto(tuple.getT1()));
});
}
test:
#Mock
private IMongoBankAccountRepository mongoRepository;
#InjectMocks
private BankAccountServiceImpl bankAccountServiceImpl;
#Test
void saveBankAccountAndRoleTest() throws Exception {
when(mongoRepository.findAll()).thenReturn(Flux.just(bnkDto)
.map(AppUtils::dtoToEntity));
when(mongoRepository.findAll().filter(ba ->
ba.getCustomerId().equals(customerId)))
.thenReturn(Flux.just(bnkDto).map(AppUtils::dtoToEntity));
StepVerifier.create(bankAccountServiceImpl.findAccountsByCustomerId(customerId))
.expectSubscription()
.expectNext(bnkDto)
.verifyComplete();
var spy = PowerMockito.spy(bankAccountServiceImpl);
PowerMockito.when(spy, "sizeAccounts", customerId)
.thenReturn(Mono.just(2L));
PowerMockito.when(spy, "saveBankAccountAndRole",bnkDto)
.thenReturn(Mono.just(bnkDto));
}
exception:
java.lang.AssertionError: expectation "expectNext(com.nttdata.bootcamp.model.dto.BankAccountDto#147c4523)" failed (expected value: com.nttdata.bootcamp.model.dto.BankAccountDto#147c4523; actual value: com.nttdata.bootcamp.model.dto.BankAccountDto#551725e4) at com.nttdata.bootcamp.business.impl.BankAccountServiceImplTest.saveBankAccountAndRoleTest(BankAccountServiceImplTest.java:267)
Which sends me when verifyComplete()
By looking at the code in your test, you shouldn't expect an specific object to be returned.
when(mongoRepository.findAll()).thenReturn(Flux.just(bnkDto)
.map(AppUtils::dtoToEntity));
when(mongoRepository.findAll().filter(ba ->
ba.getCustomerId().equals(customerId)))
.thenReturn(Flux.just(bnkDto).map(AppUtils::dtoToEntity));
The code above is mapping that DTO object to an Entity, which makes sense for a repository. However, that means that the following piece of code will "remap" it to a newly created object:
.zipWhen(bnk -> {
var customerRoleDto = new CustomerRoleDto();
customerRoleDto.setBankAccountId(bnk.getBankAccountId());
customerRoleDto.setCustomerId(bnkDto.getCustomerId());
customerRoleDto.setRoleType(bnkDto.getRoleType());
return webClientRoleHelper.saveCustomerRole(customerRoleDto);
})
Thus, you should be expecting an object with that same class, containing the same instance's variables values. But you can't expect it to be the exact same object.
You might want to try this (untested code):
StepVerifier.create(bankAccountServiceImpl.findAccountsByCustomerId(customerId))
.expectSubscription()
.expectMatches(dto -> dto.getBankAccountId().equals(bankDto.getBankAccountId) && dto.getCustomerId.equals(bnkDto.getCustomerId))
.verifyComplete();
I hope that works out for you.

Spring cache for specific values #Cacheable annotation

I want to cache a result of a method only when the attribute of the result contains specific values. For example
Class APIOutput(code: Int, message: String)
sealed class Response<out T : Any> : Serializable {
data class Success<out T : Any>(val data: T) : Response<T>()
data class Error(val errorText: String, val errorCode: Int) : Response<Nothing>()
}
#Cacheable(
key = "api-key",
unless = "do something here"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
In the above method, I want to cache the response only when Response.Success.data.code == (long list of codes).
Please note, in the previous line data is nothing but APIOutput object. How could I achieve it using unless or any other approach. I was thinking of writing a function that takes a doApicall method result as input and would return true or false and call that method it as unless="call a method". But I'm not sure how to do it. Any help is highly appreciated.
You can specify an expression to be evaluated in unless using SpEL. The returned value is available as result so you can do something like -
#Cacheable(
key = "api-key",
unless = "#result!=null or #result.success.data.code!=200"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
You can even use Regex in SpEL and can create custom Expression parsers if the existing functionality is not enough for your usecase.
Thanks Yatharth and John! Below is the condition that worked for me. resultcodes in the below expression is a list
#Cacheable(
key = "api-key",
unless = "!(#result instanceof T(com.abc.Response\$Success))
or (#result instanceof T(com.abc.Response\$Success)
and !(T(com.abc.APIStatus).resultCodes.contains(#result.data.code)))"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}

Recursively filter and map a list of properties

I'm using Kotlin reflection to check if attributes that have a certain annotation are null.
Given the following example:
data class DataClass(
#SomeRandomAnnotation
val otherAnnotated: String?,
val inner: InnerClass
)
data class AnotherDataClass(
#SomeRandomAnnotation
val annotatedProperty: String?,
val dataClass: DataClass
) {
fun checkCreditAnalysisConstrain() {
print(checkConstrain(this))
}
}
And the function that checks it:
fun checkConstrain(parentClass: Any): List<String> {
val filter = parentClass::class.memberProperties.filter {
if (memberIsDataClass(it)) checkConstrain(getMemberPropertyInstance(parentClass, it))
hasAnnotation(it) && propertyIsNull(it, parentClass)
}
return filter.map { formatResult(parentClass, it) }
}
The idea is that the function is going to iterate through the attributes of my classes checking if they have the annotation and checking if the value is null.
If the property is a data class, the code evaluates the properties of the childs, recursively.
After that, I map the results, transforming the KProperty's into a simple String that is human readable, containing the class name and the attribute name.
The problem is that the above code does not work as expected. The properties returned are only the properties from the first-level class.
If, instead of doing a filter, I just run a forEach and print the result, I get the expected attributes. So I'm pretty sure it's related to the recurring inside a filter.
Do you see any way of doing this in a more functional way? I'm just concerned I won't need a "temp" list and add values to the list and reset it afterwards.
Your function recursively calls itself, but does nothing with the returned list of that recursive call. That's why you only get results for the top-level class.
Also, in my opinion, you shouldn't rely on side effects happening from your filter call. It probably works, but the function's documentation does not provide a guarantee that it will be called exactly once per item in the collection. So there should be a separate for-loop to do the recursive calls, and the result should be added onto existing results.
fun checkConstrain(parent: Any): List<String> {
val memberProperties = parent::class.memberProperties
var result = memberProperties
.filter { hasAnnotation(it) && propertyIsNull(it, parent) }
.map { formatResult(parent, it) }
memberProperties.filter { memberIsDataClass(it) }
.mapNotNull { getMemberPropertyInstance(parent, it) }
.forEach { result += checkConstrain(it) }
return result
}
You didn't provide code for several of the functions you used. This is what I used for them:
val KProperty<*>.returnTypeClass get() = this.returnType.classifier as? KClass<*>
fun <T> memberIsDataClass(member: KProperty<T>) = member.returnTypeClass?.isData == true
fun <T> getMemberPropertyInstance(parent: Any, property: KProperty<T>) = property.getter.call(parent)
fun <T> hasAnnotation(property: KProperty<T>) = property.annotations.firstOrNull { it.annotationClass == SomeRandomAnnotation::class } != null
fun <T> propertyIsNull(property: KProperty<T>, parent: Any) = getMemberPropertyInstance(parent, property) == null
fun formatResult(parent: Any, property: KProperty<*>) = "$parent's property(${property.name}) is annotated with SomeRandomAnnotation and is null."

How to exclude null value when using FsCheck Property attribute?

I need to write a simple method that receives a parameter (e.g. a string) and does smth. Usually I'd end up with two tests. The first one would be a guard clause. The second would validate the expected behavior (for simplicity, the method shouldn't fail):
[Fact]
public void DoSmth_WithNull_Throws()
{
var sut = new Sut();
Assert.Throws<ArgumentNullException>(() =>
sut.DoSmth(null));
}
[Fact]
public void DoSmth_WithValidString_DoesNotThrow()
{
var s = "123";
var sut = new Sut();
sut.DoSmth(s); // does not throw
}
public class Sut
{
public void DoSmth(string s)
{
if (s == null)
throw new ArgumentNullException();
// do smth important here
}
}
When I try to utilize the FsCheck [Property] attribute to generate random data, null and numerous other random values are passed to the test which at some point causes NRE:
[Property]
public void DoSmth_WithValidString_DoesNotThrow(string s)
{
var sut = new Sut();
sut.DoSmth(s); // throws ArgumentNullException after 'x' tests
}
I realize that this is the entire idea of FsCheck to generate numerous random data to cover different cases which is definitely great.
Is there any elegant way to configure the [Property] attribute to exclude undesired values? (In this particular test that's null).
FsCheck has some built-in types that can be used to signal specific behaviour, like, for example, that reference type values shouldn't be null. One of these is NonNull<'a>. If you ask for one of these, instead of asking for a raw string, you'll get no nulls.
In F#, you'd be able to destructure it as a function argument:
[<Property>]
let DoSmth_WithValidString_DoesNotThrow (NonNull s) = // s is already a string here...
let sut = Sut ()
sut.DoSmth s // Use your favourite assertion library here...
}
I think that in C#, it ought to look something like this, but I haven't tried:
[Property]
public void DoSmth_WithValidString_DoesNotThrow(NonNull<string> s)
{
var sut = new Sut();
sut.DoSmth(s.Get); // throws ArgumentNullException after 'x' tests
}

Returning object of type specified in method arguments instead of AnyRef

I have the following method:
#org.springframework.stereotype.Service
class EntityCacheManager {
def get(cacheId: String, entityClass: Class[_]): AnyRef = { ... }
//...
}
So to use it, i have to write this:
val cachedEntity = entityCacheManager.get(cacheId, classOf[SomeEntity]).asInstanceOf[SomeEntity]
Is there some way to make EntityCacheManager.get() returning instance of type entityClass which is specified in method params? I'd like to avoid casting asInstanceOf every time i use this method. I know it would be nice to use generic definition of type EntityCacheManager, but it's also a spring-managed bean, so i think using generics will cause troubles.
You can use a more idiomatic scala approach by using the ClassTag typeclass
class EntityCacheManager {
def get[T: ClassTag](cacheId: String): T = {
val entityClass = implicitly[ClassTag[T]].runtimeClass
val myObject: T = ??? // you retrieve your object somehow using entityClass
myObject
}
}
you can now use it like this:
val myEntityClassInstance = get[MyEntityClass]("key")

Resources