Unable to test kotlin interface with Robolectric - android

Currently, I am using a testing framework called Robolectric. And I was wondering what is the right way to test an interface to check the value and method for it. For example, In the following sample, I have an interface called TestInterface. And I wanted to test if this interface will work. So I set up like the following, by making the interface into an object. Is it a wrong way to do it ? Or is there a better approach? I'm not savvy with testing. So if there are some samples or hints, it will be so helpful. Thank you.
#RunWith(RobolectricTestRunner::class)
class MySampleTest {
#Test
fun `test mysample `() {
val testName = "Test Name"
val userInput = "User Input"
val testResults = object : TestInterface {
override val testValue: String
get() = testName
override fun createTest(input: String): String {
return userInput+input
}
}.createTest(userInput)
assertTrue(keyPair.verify(userInput, testResults))
}

As we are not talking about UI, saying "test interface" confuses me. We can write tests for implementations only. Otherwise what we can test at all?
There should be one or more implementations in you project (if don't, why do even need this interface?), so you can test them.

Related

How to write test cases for class using netrypoint

I am evaluating if I can use hilt in my app. One blocker is to understand how to write test cases for class that is using entry points. I am not able to find anything related to it. Below is an example of a class method that I would like to unit test but I am not able to understand how to do it. Please help.
suspend fun processResponse(context: Context, requestType: String, response: NetworkResponse) {
val obj = EntryPointAccessors.fromApplication(
appContext, UtilitiesEntryPoint::class.java)
someDependency = obj.getSomeDependency()
//some business logic that need to be tested

Android SparseArray is null when running a Unit Test

I have a unit test for a kotlin object which uses a SparseArray.
The test always failed because the SparseArray is always null despite it's initialization.
object Exam : KoinComponent {
var map = SparseArray<Char?>()
init {
map.put(0, 'a')
map.put(1, 'b')
map.put(2, 'c')
map.put(3, 'd')
}
fun getChar(key: Int): Char? {
Log.d(KOIN_TAG, "" + map.get(key))
return map.get(key)
}
class ExamTest : KoinTest {
#Test
fun getCharTest(){
assertEquals(Exam.getChar(0), 'a')
}
}
I debugged this test and it ran through the init of the Array, but the value is always null.
Please help me to solve this case. Thank you
The problem is, that SparseArray is part of Android and not of Java. In an unit test you are only able to use Plain old Java Objects and no android classes or dependencies (for example from package android.util).
Therefore you have to mock the SparseArray in your test.
For example you can use Mockito:
private var map: SparseArray<Char> = SparseArray()
map = mock()
Unfortunately you are not able to pass the mocked sparse array to your object.
So you cannot test your specific example like this.
Another solution would be to use the Robolectric framework if you want to test a class with platform dependencies. http://robolectric.org/

Type inference failed: Not enough information to infer parameter T in fun <T : Context!> getApplicationContext(): T! Please specify it explicitly

I am trying to write some tests for my android app and it's really chalanging for me. One of many obsticles is this error
Type inference failed: Not enough information to infer parameter T in fun <T : Context!> getApplicationContext(): T! Please specify it explicitly.
which occures on this line
val actualIntent: Intent = shadowOf(ApplicationProvider.getApplicationContext())
.nextStartedActivity
Full test code looks like this
#Test
fun clickingLogin_shouldStartLoginActivity() {
val scenario = launch(LogInActivity::class.java)
scenario.onActivity { activity ->
activity.go_to_register_button.performClick()
val expectedIntent = Intent(activity, RegistrationActivity::class.java)
val actual: Intent = shadowOf(ApplicationProvider.getApplicationContext())
.nextStartedActivity
expectedIntent.component shouldBeEqualTo actual.component
}
}
Basically the shadowOf function is overloaded and can return many thinks and I need to specify the type.
I believe it should be somethink like shadowOf<SomeType>(...) But I have no idea what the actual type should be.
Any help would be really appreciated.
EDIT
I am following roboloctric guideline but trying to write it in an androidX way
An Intent is a different type of Object that does not extend from Context.
this line :
val actualIntent: Intent = shadowOf(ApplicationProvider.getApplicationContext())
provides a Context as an argument, and returns a ShadowContext, not an Intent.
docs ref : http://robolectric.org/javadoc/3.0/org/robolectric/Shadows.html#shadowOf-android.content.Context-
Basically it is telling you that a tree can't be a type of car.
I may not have asked as clearly as I should have. But for anyone who landed here stucked with the same problem here is a solution.
I did not figure out how to test if an activity is producing correct intents in normal tests. But in instrumented tests it goes like this:
#get:Rule
var activityRule: IntentsTestRule<MyActivity> =
IntentsTestRule(MyActivity::class.java)
#Test
fun testIntent () {
// perform some actions
// than verify
intended(hasComponent(OtherActicity::class.qualifiedName))
intended(hasExtra(A_CONSTANT, someValue))
}
You need a dependency for this to work
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
more info here

Kotlin - Mockito verify method calls

I'm trying my hand with Mockito for writing unit test's. I have a class that needs to be tested like below-
open class Employee {
fun setDetails(name: String, age: Int) {
setName(name)
setAge(age)
}
fun setName(name: String) { }
fun setAge(age: Int) { }
}
Below is my test class
class EmployeeTest {
#Mock
lateinit var emp: Employee
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testDetail() {
emp.setDetails("Henry", 23)
verify(emp, times(1)).setAge(23)
}
}
Here is my problem
When I do -
verify(emp, times(1)).setAge(23)
This give's me a success, because setAge is called once in setDetails() of Employee.kt. So that works fine for me
But, when I do-
verify(emp, never()).setAge(23)
This still gives me a success, even though the method is called in setDetails(). Shouldn't this test case fail?
Please help me understand this. I haven't been able to figure out why this happens.
EDIT
Here's what worked for me
I used a spy instead of a mock. However, I had to also declare the methods as open in Kotlin.
As mentioned by #kcoppock, your question includes an improper use of a mock. You should be using mocks to stub out dependencies in order to control their behavior.
In your case, the unit under test is the Employee class and its associated methods. In general, you do not want to mock out the unit under test because you want to know (from your unit test) if your class is behaving the way it should. To accomplish that, you'll want to use a real instance of an Employee, and not a mock.
If you are insistent on using verify on the Employee instance, you can create a spy.
#Test
fun setDetails_adjustsAge() {
val employee = spy(Employee())
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
verify(emp, times(1)).setAge(23)
}
Here are some references for further reading:
Mockito official documentation on spies:
http://static.javadoc.io/org.mockito/mockito-core/2.24.0/org/mockito/Mockito.html#13
Tutorial on how to use Mockito.spy
https://www.baeldung.com/mockito-spy
Differences between mock and spy: https://www.toptal.com/java/a-guide-to-everyday-mockito
So your issue here is that you don't actually want to use a mock. When you use a mock, you're required to define the behavior for any method that you call on that instance. So when you call emp.setDetails("Henry", 23), there is no implementation for that method so nothing happens. The behavior defined in the Employee class will not be used, as emp is just a fake instance of Employee that has not defined any behavior.
For your test scenario, you should prefer to use a real instance, and validate the end result rather than the internal behavior. For instance:
#Test
fun setDetails_adjustsAge() {
val employee = Employee()
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
}

Clean Coroutines usage in Kotlin with Unit Test support

Since a while we're working with Kotlin and one of the things we're currently focussing on is using Coroutines to take care of operations we want to run async.
While the example usages are clear and that works, I'm having some issues integrating this in a clean manner within our architecture. When looking at a method's implementation for a domain-focussed class, the idea is that it's easy to read and there is as less "noise" as possible from async functionality. I know I can't have async, without actually using it. So writing something like this is what I'd like:
val data = someService.getData().await()
// work with data
But this is what I'd like to prevent:
launch(UI) {
val data
val job = async(CommonPool) {
data = someService.getData()
}
job.await()
// work with data
}
That, I'd like paired with practical Unit Tests for these domain-focussed classes, but I can't really get that to work. Let's look at an example:
// Some dependency doing heavy work
class ApiClient {
suspend fun doExpensiveOperation(): String {
delay(1000)
return "Expensive Result Set"
}
}
// Presenter Class
class Presenter(private val apiClient: ApiClient,
private val view: TextView) {
private lateinit var data: String
fun start() {
log("Starting Presenter")
runBlocking {
log("Fetching necessary data")
data = apiClient.doExpensiveOperation()
log("Received necessary data")
}
workWithData()
log("Started Presenter")
}
fun workWithData() {
log(data)
}
private fun log(text: String) {
view.append(text+"\n")
}
}
// In an Activity
val presenter = Presenter(ApiClient(), someTextView)
presenter.start()
That works (screenshot: https://imgur.com/a/xG9Xw). Now lets look at the test.
class PresenterTest {
// ... Declared fields
#Before
fun setUp() {
// Init mocks (apiClient, textView)
MockitoAnnotations.initMocks(this)
// Set mock responses
runBlocking {
given(apiClient.doExpensiveOperation()).willReturn("Some Value")
}
presenter = Presenter(apiClient, textView)
}
#Test
#Throws(Exception::class)
fun testThat_whenPresenterStarts_expectedResultShows() {
// When
presenter.start()
// Then
Mockito.verify(textView).text = "Some Value\n"
}
}
Now this test is less than ideal, but regardless, it never even gets to the point where it can verify things work as intended, because lateinit var data wasn't initialized. Now ultimately the aesthetics and readability of our domain classes is simply how far I want to go, which I have some practical working examples for that I'm happy with. But making my tests work seems to be challenging.
Now there's some different write-ups online about this kind of stuff, but nothing has really worked out for me. This (https://medium.com/#tonyowen/android-kotlin-coroutines-unit-test-16e984ba35b4) seems interesting, but I don't like the idea of a calling class launching a context for a presenter, because that in turn has a dependency that does some async work. Although as an abstract thought I like the idea of "Hey presenter, whatever you do, report back to me on a UI context", it rather feels as a fix to make things work, leading to a shared concern for async functionality across different objects.
Anyway, my question:
Moving away from the short examples, does anyone have any pointers on how to integrate coroutines within a bigger architecture, with working unit tests? I'm also very open to arguments that make me alter my way of viewing things, given that's it's convincing on a different level than "If you want things to work, you have to sacrifice.". This question goes beyond just making the example work, as that is just an isolated example, while I'm looking for a real solid integration within a big project.
Looking forward to your input. Thanks in advance.
I'd suggest an approach of having some kind of AsyncRunner interface and have two implementations of this AsyncRunner interface. One would be implementation for Android, using launch(UI), and the other would be some blocking implementation, using runBlocking.
Passing the right type of AsyncRunner into code run within app and code run in unit test should be done by dependency injection. In your code then, you'd not use coroutines directly, instead you'd use injected AsyncRunner to run asynchronous code.
Example implementations of this AsyncRunner might look like this:
interface AsyncRunner {
fun <T>runAsync(task: () -> T, completion: (T) -> Unit)
}
class AndroidCoroutineAsyncRunner: AsyncRunner {
override fun <T>runAsync(task: () -> T, completion: (T) -> Unit) {
launch(UI) {
completion(async(CommonPool) { task() }.await())
}
}
}
class BlockingCoroutineAsyncRunner: AsyncRunner {
override fun <T>runAsync(task: () -> T, completion: (T) -> Unit) {
runBlocking {
completion(async(CommonPool) { task() }.await())
}
}
}
where the task parameter represents the thread blocking code (for example fetching data from API) and completion parameter will get data from the task and do something with them.
You should abandon coroutines and use RxJava instead. There you will find the kind of conciseness and simplicity you seek. When I ask most developers why they use coroutines, their answer is always the same: "Well, coroutines are the new, new thing, and we should use the latest technology from Google". Except that coroutines are not new. They were first introduced in about 1952 (See "Coroutines" in Wikipedia) as a proposal for doing asynchronous software development. It is pretty clear that the Computer Science community rejected coroutines years ago as not being the best approach for asynchronous programming. Why JetBrains decided to introduce an old, rejected technology into Kotlin is something you will have to ask JetBrains. I have had to deal with coroutines in code that others have written for several years now, and I always find coroutines to be needlessly complex. There is no way that coroutines do anything more than decrease maintainability when maintenance developers have to deal with coroutine spaghetti written by a developer who has long since departed the project.
The next thing I hear from these same developers is that RxJava is old technology and coroutines are new technology. If they had done their research, they would never have made such an outrageously incorrect statement. IMHO, RxJava is the most important new development in asynchronous software development in the entire history of computer science.

Categories

Resources