Matt's Tidbits (9 Part Series)
Last week I wrote about a strategy for debugging complex unit test failures. This time, I’d like to tell you about a strange code coverage issue with Kotlin and how to resolve it!
If you’re writing unit tests for a Kotlin class, you may not see complete code coverage for code like this (even if you’re testing
bar() with both null & non-null values of
foo is a var, the compiler can't guarantee that another thread won't access it and change its value between the time you check
if(foo != null) and the time you call
foo?.size(). This is why you have to use the
? when calling
foo.size() - because the compiler won't smart cast the
String even though you're inside a null-checking if statement.
Unless you want to write tests that actually capture the value of
foo changing mid-method, it's much better to write this code like this:
In this 2nd example, the compiler automatically creates a constant
val reference to
foo that's internal to the
let block, and can therefore guarantee that
it inside the
let block) is non-null. This also eliminates the code coverage issue as the value is only checked for null once.
In my opinion, this approach is also much cleaner than handling it yourself by manually creating a temporary
val like this:
What was initially baffling about this was that I was using a nullable
var such as in the example above, and this class only had two methods which both had
synchronized blocks wrapping all changes to
foo. It took me a while to tumble to the fact that the compiler was worried about multi-threaded access, and that this was one instance where the code coverage tool just isn't smart enough to be able to determine that multithreaded access was not a concern.
Have you discovered other strange/unexpected behaviors like this with Kotlin related to code coverage? If so, how have you solved them? Please let me know in the comments below! And, please follow me on Medium if you’re interested in being notified of future tidbits.
This tidbit was originally delivered on October 12, 2018.