Unexpected assert behavior - android

I'm new to Kotlin Android so in writing tests these asserts unexpectedly pass:
import org.junit.Test
assert("x".equals("y"))
assert("x" == "y")
but this does fail:
import junit.framework.Assert.assertEquals
assertEquals("x", "y")
So I reviewed string structural comparisons.
And then found that this also passes:
assert(false)
Looks like org.junit.Test comes by default in a new project via:
testImplementation 'junit:junit:4.12'
So now I'm wondering what's the correct testImplementation package to sync with. Am I using the wrong one?

From the doc for assert (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/assert.html):
Throws an AssertionError if the value is false and runtime assertions
have been enabled on the JVM using the -ea JVM option.
What you should do is to use Assert.assertTrue or Assert.assertEquals from org.junit instead.

You're using kotlin.assert which are only enabled when platform assertions are enabled. The checks that you say should cause errors simply are never executed.
Either run it with the -ea (enable assertsions) JVM parameter or use assertEquals which is the usual test framework name since assert is a keyword in java.

Looking for inside documentation, assertEquals hasn't two String parameters to compare. It only receive generic Objects:
assertEquals(java.lang.Object expected, java.lang.Object actual) =>
Asserts that two objects are equal.
Strings should be compared using .equals(String) method due using Objects have differents address memory access, so these are differents despite of they have same content.
:: Edit ::
To have the sames Objects type of Strings you should use .clone() method. It returns same content and same references from original object.

Related

Android Studio 4.2.2 - Why debbuger doesn't evaluate parameter in a inner funcion?

Android Studio 4.2.2 evaluates a local and global variable, but doesn't evaluate parameter funcion when is inside a inner function.
Until the previous version this worked perfectly.
fun a(p:param) {
fun b(){
var v = p+1 // Here
}
}
Suppose that one try to evaluate the parameter p in the line with comment
// Here with Alt F8
The message in evaluate window is
Cannot find the local variable 'p' with type
This hurts a lot because it forces you to replicate the parameter as a local variable in each routine to be visible in the debugger.
var p = p
Has anyone noticed this? Is there any workaround?
Notice that Variables windows display a parameter with $ prefix, but it also doesn't work in evaluate window.
I've posted this issue in JetBrains.
First things first:
Is there any workaround?
Yes, bind the parameter p to a local variable inside b:
fun a(p: Param) {
fun b() {
val p = p
var v = p + 1
}
}
and it will all work as expected.
The root cause is slightly more convoluted.
Kotlin grew up on a language tool chain tied very closely to the IntelliJ language plug-in for Kotlin. The original JVM compiler was a code generator that consumed the data structures of semantic information used by the IntelliJ language plug-in for diagnostics, quick fixes, and so on.
This architecture was very good for providing language support to an IDE, but less so for building a fast batch compiler.
With Kotlin 1.5, the "IR backend" was enabled as the default code generator for JVM. It uses a more traditional compilation approach of gradually translating an abstract syntax tree (AST) to progressively simpler intermediate languages before outputting JVM byte code.
With this change, a number of new compilation strategies were implemented. In particular, in the old strategy, local functions were treated as lambdas bound to a local variable. Their free variables were recorded in the state of the allocated Function object at the declaration site. When the local function is called, it translates to a call to invoke on that function object. End of story.
In the new approach, local functions are lifted into private static functions on the same class as the outer function. Free variables in the local function are closed by parameters to that lifted function, and instead of recording them at the declaration site in a lambda object, they are passed at the call site as arguments.
In your example. the p is free in the inner function b, so an additional parameter $p is added to b.
While this works for "release builds", the surrounding tooling has not caught up until recently. The "Evaluate Expression..." mechanism has been hit particularly hard, as it's quite sensitive to the layout and shape of the resulting JVM byte code.
In this specific case, it was a matter of adjusting the mechanism in the debugger that maps free variables of the fragment to local variables at the breakpoint. With this change aimed at 2022.3, you should hopefully stop noticing this specific bug, and a host of other improvements as a new and revised version of the evaluation mechanism ships.

ArgumentMatchers.any must not be null

I am trying to test ViewModel to make sure livedata gets updated correctly. However when using ArgumentMatchers.any() it fails with IllegalStateException saying:
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not
be null
#Test
fun emitValueIfCountIs7() {
doAnswer { invocation: InvocationOnMock ->
val subscriber: mViewModel.CountSubscriber = invocation.getArgument(0)
subscriber.onNext(7)
null
}.`when`(countUseCase).execute(
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java),
ArgumentMatchers.any(Parameters::class.java)
)
// When
mViewModel.getCount()
// Verify
assert(mViewModel.countResponse.value != null)
}
I am using Kotlin and have the following dependencies:
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-inline:2.23.4"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
Here are my imports:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.doAnswer
import io.reactivex.Observable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.invocation.InvocationOnMock
Strange thing is that it used to work before, and I don't know what has happened that could affect this.
Getting matchers to work with Kotlin can be a problem.
If you have a method written in kotlin that does not take a nullable parameter, then we cannot match with it using Mockito.any().
This is because it can return void and this is not assignable to a non-nullable parameter.
If the method being matched is written in Java, then I think that it will work as all Java objects are implicitly nullable.
One possible solution would be to use a library like mockito-kotlin
But you can solve this issue easily with a few lines of code yourself.
If you need typed any(type: Class)
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
OR
You can use this matcher instead of Matchers.any() :
object MockitoHelper {
fun <T> anyObject(): T {
Mockito.any<T>()
return uninitialized()
}
#Suppress("UNCHECKED_CAST")
fun <T> uninitialized(): T = null as T
}
and use MockitoHelper.anyObject() instead of any() in your kotlin tests.
You can find more information in this post: Using Mockito with Kotlin
There is a discussion about possible solutions in this post :
Is it possible to use Mockito in Kotlin?
The correct solution is mentioned in the comment section of the question by #Ana Koridze. Yes, if you are using Koltin + mockitoKotlin. Make sure you are using the following import:
1 - Using the Mockito-kotlin:
import org.mockito.kotlin.any from Mockito-kotlin
instead of
import org.mockito.Mockito.any
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
2 - Or if you are using older mockito kotlin version original created by nhaarman before the intergation
import com.nhaarman.mockitokotlin2.any from nhaaram's Mockito-kotlin instead of import org.mockito.Mockito.any
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin2_version"
BTW, if you are using Android Studio or IntelliJ IDEA. the any() from mockitokotlin library should be italic by default style/color scheme.
Notice the any() at the end of line. This is from mockitokotlin
And here is the any() from mockito
Thanks #Sattar for the recommended edit.
mockito-kotlin has added support for nullable args with anyOrNull()
`when`(myMock.doesThis(any(), anyOrNull())).thenReturn(someObject)
use Mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
this will use any that works for kotlin as this is a wrapper lib for Mockito but for kotlin
This is what worked for me,
either replaced all generic any()s , with a specific anyTYPE(), i.e anyInt(), anyList() etc from core lib org.mockito:mockito-core and it fixes the (nullability)issue, it seems the specific definitions with type can handle null. this option does not require you to import any extra lib
or
if you really need to use the generic type any() , add this official Mckito extension lib https://github.com/mockito/mockito-kotlin and make sure you use the any() from this lib (by making sure your imports has this in it import org.mockito.kotlin.any)
I suggest to start using MockK lib https://github.com/mockk/mockk instead of Mockito as it is a Mock library for Kotlin = MockK)
however, if you feel lazy to switch right now or maybe dealing with legacy tests (as in my case :), this should fix your issue too.
For me all solutions above were not enough - in addition to that I had to mark the called method as an 'open' method.
According to this:
https://github.com/mockito/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null
The method is final and Mockito couldn't mock it so I had to add 'open'.
I wrote a simple wrapper function around Mockito's any() and got rid of the warning.
private fun <T> any() : T {
return org.mockito.ArgumentMatchers.any()
}
I am still pretty green at Kotlin though, so I am not sure whether there may be some unwanted side effects. I put this answer out there in case it helps anyone or someone gives me feedback.

Can we add a #Suppress flag globally?

In kotlin, if we use a when block on an enum while exhausting all the enum values but without adding an else branch, we get this warning: "Enum argument can be null in Java, but exhaustive when contains no null branch".
Android Studio then suggest to add the suppress flag #Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") either to the statement, function, class or file scope.
Is there any way to add this to the project scope ? Since my project is 100% Kotlin I don't need this warning and would like to disable it globally.

Package Level Protection in Kotlin [duplicate]

In Java, we have the package protected (default) modifier for classes, which allows us to have many classes in a single package but exposes only a few and keeps the logic encapsulated.
With Kotlin this doesn't seem to be the case. If I want a few classes to be visible to each other but no further, I have to use a private modifier which limits visibility to a single file.
So if you want 10 classes in a package but only one of them to be public, you'd have to have one huge file with all the classes in it (and private all over the place).
Is this normal practice or there is a way to achieve some similar modularity in Kotlin?
I don't understand: if they have the notion of a package, why did they get rid of package protected access?
Update: We might have package protected visibility after all
see the discussion here
Update: If you read through the discussion and still think this is a must-have feature for the language, please vote here
Kotlin, compared to Java, seems to rely on packages model to a lesser degree (e.g. directories structure is not bound to packages). Instead, Kotlin offers internal visibility, which is designed for modular project architecture. Using it, you can encapsulate a part of your code inside a separate module.
So, on top level declarations you can use
private to restrict visibility to the file
internal to restrict visibility to the module
At this point, there is no other option for visibility restriction.
As a workaround for me on android I've created #PackagePrivate annotation and lint checks to control access. Here you can find the project.
Lint checks are obviously not that strict as compiler checks and some setup needed to fail the build on errors. But android studio picks up lint checks automatically and shows error immediately while typing. Unfortunately I don't know a way to exclude annotated members from autocomplete.
Also, as lint is a purely compile-time tool, no checks at runtime performed.
As #hotkeys points out, you can use the internal keyword in a module or you can put all classes that would otherwise belong in a package inside a single file, but sticking several classes in a file may be a questionable design decision.
For me, the package visibility is helpful for its documenting value. I want to know what public interface some package is presenting to the rest of the project, hide factory implementation classes and so on.
So even if it's possible to access package-private classes and methods in Java, I still choose to use the package modifier.
For this I created a project with a single annotation:
package com.mycompany.libraries.kotlinannotations;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Documented
#Retention(SOURCE)
#Target({ TYPE, METHOD, CONSTRUCTOR })
/**
* Use in Kotlin code for documentation purposes.
*
* Whenever a Kotlin class or method is intended to be accesible at package level only.
*
*/
public #interface PackagePrivate {
}
Then I can use this annotation in any Kotlin project.
The second step, which I haven't done yet, is creating a PMD rule to enforce this with maven (or any other build tool for that matter) and also be able to see violations of the rule in my IDE with the pmd plugin.
There no is full Kotlin support in pmd at this moment but it seems to be expected at some point.
A near-replacement for package private visibility is available using the opt-in requirements feature (credit to pdvrieze on Kotlin discussions). This is the annotation syntax typically used to flag experimental features in an API.
To use it, create an annotation denoting package private declarations:
#RequiresOptIn(message = "Only to be used in MyPackage")
#Retention(AnnotationRetention.BINARY)
annotation class MyPackagePrivate
Then annotate any methods you want to be package private with it:
#MyPackagePrivate
fun aPackagePrivateMethod() {
// do something private within a package
}
In this way a warning will be generated on any method that calls the annotated method unless the calling method is itself annotated with the corresponding #OptIn annotation, here shown at class level:
#OptIn(MyPackagePrivate::class)
class AClassInThePackage {
fun userOfPackagePrivateMethod() {
aPackagePrivateMethod()
}
}
This, then, produces a similar effect to Java's package private, except that calling methods need to explicitly opt in to using a package private declaration.
If it is desired to generate an error rather than a warning, the level parameter of #RequiresOptIn can be specified:
#RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Only to be used in MyPackage")
// annotation declaration as before
Package-based protection is pointless in Kotlin because packages themselves are unprotected
In Java, package was tied to directory structure. So if you put your classes in com\example\yoursecretengine, any attempt (deliberate or accidental) to add a rogue class there would be easily noticeable. This is the kind of security we've depended on.
Kotlin removes the ties between directory and package, so I can put my class in "my" directory (eg.src\java\pl\agent_l\illegalaccess) yet declare its package as com.example.yoursecretengine - and gain access to all the properties you've meant as package private.
In fact, a Kotlin project works perfectly without ANY package declarations. This only highlights that packages are "more what you'd call guidelines than actual rules". They're a convenience feature, useful only to unclutter namespace and nothing more.
Relevant quotes from kotlinlang:
unlike many other languages, Kotlin packages do not require files to have any specific locations w.r.t. itself; the connection between a file and its package is established only via a package header.
And:
an absence of a package header in a file means it belongs to the special root package.

Mocking kotlin property with accessors in Mockito

I have a token property in my application class(Kotlin) that is based on a SharedPreferences value
var token : String?
get() = PreferenceManager.getDefaultSharedPreferences(applicationContext)
.getString(TOKEN_PEREF_TAG, null)
set(value) {
PreferenceManager.getDefaultSharedPreferences(applicationContext)
.edit()
.putString(TOKEN_PEREF_TAG, value)
.apply()
}
The problem is that I can't set a mock value like this:
whenever(app.token).thenReturn("token")
since I get the error
java.lang.RuntimeException: Method getDefaultSharedPreferences in android.preference.PreferenceManager not mocked.
Shouldn't the mock just return the provided string?
how can I get around this error?
You can fix this error by using the mockito-inline dependency instead of the mockito-core dependency. This uses a different mocking method that circumvents this issue of the platform classes not being available. It's also particularly useful because it allows you to mock final classes, therefore eliminating the need to put every one of your classes behind an interface or mark them as open in Kotlin.
This inline mocking method can also be turned on by a configuration file, however I found just using the inline dependency much more reliable.
In a 'small test' (jUnit test, the one in src/test) the android framework is not present and the whole framework is just a stub and provides no functionality. You either have to create your own mocked SharedPreferences and PreferenceManager using mockito or sth similar. Or use an 'medium test' (instrumented test, the one in src/androidTest) which must run on emulator or a device. For more on this check Fundamentals of Testing

Categories

Resources