Can't mock `willReturn` with Optional in Kotlin

1.2k views Asked by At

I have a method

fun getUser(userId: UserId): Optional<User?> = userRepository.findById(userId)

that returns an Optional in Java. So I want to mock that method call in my Kotlin unit test.

This was my first guess...

given { mockedUserService.getUser(currentUser.userId) }.willReturn(Optional.of(currentUser))

... but the compiler says no

Type mismatch: inferred type is Optional but Optional<User?>! was expected

So I started to make the val currentUser: User? just to make the compiler happy.

given { currentUser?.userId?.let { mockedUserService.getUser(it) }.willReturn(Optional.of(currentUser))

Type mismatch: inferred type is Optional but Optional<User?>? was expected

Type mismatch: inferred type is User? but TypeVariable(T) was expected

And now I am somewhat lost. How can I make the compiler happy?

3

There are 3 answers

0
Willi Mentzel On BEST ANSWER

One way would be to fix the test, but that is actually not the problem here. You should avoid Optional whenever possible in Kotlin. Java's type system has no way to distinguish nullable and non-nullable values. Kotlin does, so you should transform the Optional<T> as early as possible to T?.

You can easily fix your function like that:

fun getUser(userId: UserId): User? = userRepository.findById(userId).orElse(null)
2
Sebastian Heikamp On

You could try willAnswer instead of willReturn:

given { mockedUserService.getUser(currentUser.userId) }.willAnswer { Optional.of(currentUser) }
3
ChristianB On

Consider this as an alternative. I am assuming you are using Java's type Optional and not your own implementation (doing more than I can see here).

The Optional came to Java to avoid a NPE and indicate an abscent of a type at runtime. But in Kotlin you do not really need an Optional, since you can explicitely define your type as nullable T?.

So you can either define it as:

fun getUser(userId: UserId): User?

or

fun getUser(userId: UserId): Optional<User> // not nullable!