Multithreaded testing in a Kotlin Android Projects, Alternative to sleep() - android

I have the following test code;
class MainActivityTest {
#get:Rule
var mActivityTestRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
#Test
fun changeActivityOrientationToLandscape() {
mActivityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
Thread.sleep(5000)
val view = mActivityTestRule.activity.findViewById<ConstraintLayout>(R.id.fragment_layout)
assertNotNull(view)
}
#Test
fun changeActivityOrientationToPortrait() {
mActivityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
Thread.sleep(5000)
val view = mActivityTestRule.activity.findViewById<DrawerLayout>(R.id.drawer_layout)
assertNotNull(view)
}
}
This works fine, but I think there is probably a better way to handle it. I would like the first line of both tests to finish executing before getting a view object, currently I do that by making the thread to sleep Thread.sleep(5000).
This method assumes that the the orientation changed within 5 seconds, which isn't necessarily the case, and it is very slow.
I have tried searching for the answer but everything seems to use different additional frameworks or require additional program code, this seems like something that should be simple and frequently done within kotlin and I'm suprised I haven't been able to find anything simpler to help.

Related

Is there a difference between using GlobalScope.launch and CoroutineScope().launch to launch a coroutine?

There are different ways of launching a coroutine in Kotlin. I found a couple of examples where GlobalScope and CoroutineScope are used. But the latter one is being created directly when launching a coroutine:
Using GlobalScope:
fun loadConfiguration() {
GlobalScope.launch(Dispatchers.Main) {
val config = fetchConfigFromServer() // network request
updateConfiguration(config)
}
}
Using CoroutineScope instances, created directly when launching a coroutine:
fun loadConfiguration() {
CoroutineScope(Dispatchers.Main).launch {
val config = fetchConfigFromServer() // network request
updateConfiguration(config)
}
}
In this case is there a difference between these two approaches?
Doesn't the second case violate the principle of structured concurrency?
Doesn't the second case violate the principle of structured concurrency?
Actually both cases violate it equally, because they have pretty much the exact same semantics. All the warnings against using GlobalScope.launch that you may have encountered, apply to CoroutineScope().launch just the same. A slight difference is that the latter is entirely pointless, whereas the former at least has some legitimate use cases.
Writing CoroutineScope().launch instead of GlobalScope.launch obscures the intent and consequences, as well as creates a needless object every time.
I think the CoroutineScope().launch idiom came into being solely because it circumvented the warnings you get for GlobalScope.launch.

Save file with Kotlin Coroutines in Android shows inappropiate blocking method call

I created a function for saving an image to the internal files directory. I execute this function in the lifecycleScope provided by Android like following:
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
saveImage(id, proxyEntity)
}
}
This is my function where I want to save the image.
private suspend fun saveImage(
id: Long,
proxyEntity: ProxyEntity,
) {
val entitiesDirectory = File(filesDir, "local/entities")
if (false == entitiesDirectory.isDirectory) {
entitiesDirectory.mkdirs()
}
if (null != selectedImage) {
withContext(Dispatchers.IO) {
val entityFile = File(entitiesDirectory, "$id")
val fileOutputStream = FileOutputStream(entityFile)
//selectedImage is of type Bitmap
fileOutputStream.write(ImageUtil.convertBitmapToByteArray(selectedImage!!))
fileOutputStream.close()
proxyEntity.imagePath = "local/entities/${id}"
}
}
}
The code itself is working, but my question is, why Android-Studio still shows me "Inappropriate blocking method call" and if I am doing anything wrong.
EDIT:
The message is appearing on FileOutputStream(entityFile) and the write and close function.
UPDATE: After reading Tenfour04's comment
At first I was hesitant, because in theory, suspend doesn't imply anything about the context. But like most things in life, after thinking more about it (and sleeping), I think it makes sense in a practical way to make some controlled and consistent choices. Among them, to ensure:
That you can replace the Dispatcher in a Unit Test (this means: don't hardcode, instead, inject it so you can replace it).
E.g.:
instead of:
...launch(Dispatchers.IO) { ... }
Do:
class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {
suspend fun xyz() = withContext(defaultDispatcher) { ... }
You get the idea.
Suspend functions should be safe to call from main thread
This is an interesting choice but I understand why. And I will start doing this in all my code where I can.
Source: Suspend functions should be safe to call from the main thread.
I suggest you read the Coroutines Best Practices recently updated/published by Google. We may all have our disagreements and use a different patterns here and there, but overall, I'd do what Google suggests; in the end, it's their platform and the closer you are to their code, the easier will be to deal with the incessant changes and deprecation that happen in modern development these days.
UPDATE: I haven't had coffee.
Ok, after re-reading your code, I noticed you do specify the Coroutine Context.
That being said, I'd make the context assignment the way I did it (in the viewmodel launch's call).
You don't want your code to "care" which context is used on, instead let the viewmodel decide what's best.
Original Response
The I/O operations on the main thread are a bad idea (if not forbidden by some APIs), so you need to change the scheduler
Your code:
lifecycleScope.launch {
saveImage(id, proxyEntity)
}
Is running on the Default scheduler (likely Main Thread).
Try instead to use the I/O one:
lifecyleScope.launch(Dispatchers.IO) {
saveImage(id, proxyEntity)
}

Unit test a helper class around SharedPreference

I have a helper class to save user object to shared preferences. I have used a serialize(): String function and a create(serializedString: String) function in my User data model. They use GSon serializer and are working good as suggested by the unit tests on them.
Now my helper class is called SharedPreferenceUserStore.kt which takes a Context object. The code is:
class SharedPreferenceUserStore(context: Context) {
companion object {
val TAG = SharedPreferenceUserStore::class.java.simpleName
}
var userLocalSharedPref: SharedPreferences =
context.getSharedPreferences(USER_LOCAL_STORE_SHARED_PREF_NAME, Context.MODE_PRIVATE)
/*
Store the required data to shared preference
*/
#SuppressLint("ApplySharedPref")
fun storeUserData(user: User) {
val userLocalDatabaseEditor = userLocalSharedPref.edit()
val serializedData = user.serialize()
userLocalDatabaseEditor.putString(
USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY,
serializedData
)
if (userLocalDatabaseEditor.commit()) {
Log.d(TAG, " Store Commit return true")
}
}
/*
Clear all the locally stored data from the shared pref
*/
#SuppressLint("ApplySharedPref")
fun clearUserData() {
val userLocalDatabaseEditor = userLocalSharedPref.edit()
userLocalDatabaseEditor.clear()
userLocalDatabaseEditor.commit()
}
fun getLoggedInUser(): User? {
val stringUser = userLocalSharedPref.getString(
USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
return if (stringUser==null || stringUser == ""){
null
} else{
User.create(stringUser)
}
}
And I have written some unit tests for this helper class as follows:
#RunWith(JUnit4::class)
class SharedPreferenceUserStoreTest {
lateinit var sharedPreferenceUserStore: SharedPreferenceUserStore
lateinit var user: User
//to be mocked
lateinit var sharedPreferences: SharedPreferences
lateinit var sharedPreferencesEditor: SharedPreferences.Editor
lateinit var context: Context
#Before
fun setUp() {
//mocking Context and SharedPreferences class
context = mock(Context::class.java)
sharedPreferences = mock(SharedPreferences::class.java)
sharedPreferencesEditor = mock(SharedPreferences.Editor::class.java)
//specifying that the context.getSharedPreferences() method call should return the mocked sharedpref
`when`<SharedPreferences>(context.getSharedPreferences(anyString(), anyInt()))
.thenReturn(sharedPreferences)
//specifying that the sharedPreferences.edit() method call should return the mocked sharedpref editor
`when`(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor)
//specifying that the sharedPreferencesEditor.putString() method call should return the mocked sharedpref Editor
`when`(sharedPreferencesEditor.putString(anyString(), anyString())).thenReturn(
sharedPreferencesEditor
)
`when`(sharedPreferences.getString(anyString(), anyString())).thenReturn("")
//instantiating SharedPreferenceUserStore from the mocked context
sharedPreferenceUserStore = SharedPreferenceUserStore(context)
user = User(
35,
"Prashanna Bhandary",
"prashanna.bhandary#gmail.com",
"dd58a617ea618010c2052cb54079ad67.jpeg",
"98********",
"test address 01",
1,
"yes",
"2019-08-30 04:56:43",
"2019-08-30 05:14:47",
0
)
}
#After
fun tearDown() {
}
#Test
fun passUser_storeUserData() {
sharedPreferenceUserStore.storeUserData(user)
verify(sharedPreferencesEditor).putString(
Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY,
user.serialize()
)
verify(sharedPreferencesEditor).commit()
}
#Test
fun testClearUserData() {
sharedPreferenceUserStore.clearUserData()
verify(sharedPreferencesEditor).clear()
}
#Test
fun testGetLoggedInUser_storeNotCalled() {
//calling getLoggedInUser() without calling storeUserData() should give null
assertEquals(null, sharedPreferenceUserStore.getLoggedInUser())
//verify that getString() was called on the shared preferences
verify(sharedPreferences).getString(Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
}
#Test
fun testGetLoggedInUser_storeCalled(){
//call getLoggedInUser(), we are expecting null
assertNull(sharedPreferenceUserStore.getLoggedInUser())
//verify that getString() was called on the shared preferences
verify(sharedPreferences).getString(Constants.USER_LOCAL_STORE_SHARED_PREF_SERIALIZED_DATA_KEY, "")
}
}
As I am really new to Unit Testing and Mocking libraries like Mockito. Now my question is are my tests any good? and I wanted to test if the getLoggedInUser() funciton of my helper class is doing what it is supposed to do (ie. get logged in user if shared pref has it), how do I do that?
In addition do suggest me any improvements I can make to my test or the helper class itself. Thank you.
Judging your test for what it is - A unit test running on a host machine with Android dependencies mocked with Mockito - it looks fine and like what you would expect.
The benefit-to-effort ratio of such tests are debatable, though. Personally I think it would be more valuable to run such a test against the real SharedPreferences implementation on a device, and assert on actual side effects instead of verifying on mocks. This has a couple of benefits over mocked tests:
You don't have to re-implement SharedPreferences with mocking
You know that SharedPreferenceUserStore will work with the real SharedPreferences implementation
But, such tests also have a debatable benefit-to-effort ratio. For a solo developer project, think about what kind of testing that is most important. Your time is limited so you will only have time to spend on writing the most important kind of tests.
The most important kinds of tests are the ones that test your app in the same way your users will use it. In other words, write high-level UI Automator tests. You can write how many mocked or on-device unit tests as you want. If you don't test that your entire app as a whole works, you will not know that it works. And if you don't know that your app as a whole works, you can't ship it. So in some way you have to test your app in its entirety. Doing it manually quickly becomes very labour intensive as you add more and more functionality. The only way to continually test your app is to automate the high-level UI testing of your app. That way you will also get code coverage that matters.
One big benefit of high-level UI testing that is worth pointing out is that you don't have to change them whenever you change some implementation detail in your app. If you have lots of mocked unit tests, you will have to spend a lot of time to refactor your unit tests as you refactor the real app code, which can be very time consuming, and thus a bad idea if you are a solo developer. Your UI Automator tests do not depend on low-level implementation details and will thus remain the same even if you change implementation details.
For example, maybe in the future you want to use Room from Android Jetpack to store your user data instead of SharedPreference. You will be able to do that without changing your high level UI tests at all. And they will be a great way to regression test such a change. If all you have are mocked unit tests, it will be a lot of work to rewrite all relevant unit tests to work with Room instead.
I agree with what #Enselic say about favoring Integration Test over Unit Tests.
However I disagree with his statement that this mockito test looks fine.
The reason for that is that (almost) every line in your code under test involves a mock operation. Basically mocking the complete method would have the same result.
What you are doing in your test is testing that mockito works as expected, which is something you should not need to test.
On the other hand your test is a complete mirror of the implementation itself. Which means everytime you refactor something, you have to touch the test. Preferably would be a black box test.
If you use Mockito you should try to restrict its use to methods that actually do something (that is not mocked).
Classes that generally should be mocked for testing purposes are dependencies that interact with external components (like a database or a webservice), however in these cases you are normally required to have Integration Tests as well.
And if your Integration-Tests already cover most part of the code, you can check whether you want to add a test using a mock for those parts that are not covered.
I have no official source for what I am trying to express, its just based on my experience (and therefore my own opinion). Treat it as such.
There is not much that can be said regarding the tests that guys before me haven't said.
However, one thing that you might want to consider is refactoring your SharedPreferenceUserStore to accept not Context(which is quite a huge thing, and if not handled properly could lead to unforeseen issues and/or memory leaks), but rather SharedPreferences themselves. This way, your class, that deals only with updating the prefs doesn't have access to more than it should.

Proper (and Simplified) Testing of a Data Source

I've recently started getting into testing (TDD) and was wondering if anyone can shed some light into the practice I'm doing. For example, I'm checking if the location provider is available, I implement a contract (data source) class and a wrapper, like so:
LocationDataSource.kt
interface LocationDataSource {
fun isAvailable(): Observable<Boolean>
}
LocationUtil.kt
class LocationUtil(manager: LocationManager): LocationDataSource {
private var isAvailableSubject: BehaviorSubject<Boolean> =
BehaviorSubject.createDefault(manager.isProviderEnabled(provider))
override fun isAvailable(): Observable<Boolean> = locationSubject
}
Now, when testing, I'm not sure how to proceed. First thing that I did was mocking the LocationManager and the isProviderEnabled method:
class LocationTest {
#Mock
private lateinit var context: Context
private lateinit var dataSource: LocationDataSource
private lateinit var manager: LocationManager
private val observer = TestObserver<Boolean>()
#Before
#Throws(Exception::class)
fun setUp(){
MockitoAnnotations.initMocks(this)
// override schedulers here
`when`(context.getSystemService(LocationManager::class.java))
.thenReturn(mock(LocationManager::class.java))
manager = context.getSystemService(LocationManager::class.java)
dataSource = LocationUtil(manager)
}
#Test
fun isProviderDisabled_ShouldReturnFalse(){
// Given
`when`(manager.isProviderEnabled(anyString())).thenReturn(false)
// When
dataSource.isLocationAvailable().subscribe(observer)
// Then
observer.assertNoErrors()
observer.assertValue(false)
}
}
This works. However, during my research on how to do this and that, the time I spent figuring out how to mock the LocationManager was big enough to (I think) break one of the common rules in TDD -- a test implementation should not consume too much time.
So I figured, would it be best (and still within the TDD scope) to just test the contract (LocationDataSource) itself? Mocking dataSource and then replacing the test above with:
#Test
fun isProviderDisable_ShouldReturnFalse() {
// Given
`when`(dataSource.isLocationAvailable()).thenReturn(false)
// When
dataSource.isLocationAvailable().subscribe(observer)
// Then
observer.assertNoErrors()
observer.assertValue(false)
}
This would (obviously) provide the same result without going through the trouble of mocking a LocationManager. But, I think this defeats the purpose of the test -- since it only focuses on the contract itself -- and not the actual class that uses it.
I still think that maybe the first practice is still the proper way. That initially, it just takes time to familiarize with the mocking of Android classes. But I would love to know what the experts on TDD think.
Working backwards... this looks a little weird:
// Given
`when`(dataSource.isLocationAvailable()).thenReturn(false)
// When
dataSource.isLocationAvailable().subscribe(observer)
You've got a mock(LocationDataSource) talking to a TestObserver. That test isn't completely without value, but if I'm not mistaken running tells you nothing new; if the code compiles, then the contract is satisfied.
In a language where you have reliable type checking, executed tests should have a test subject that is a production implementation. So in your second example, if observer were a test subject, that would be "fine".
I wouldn't pass that test in a code review -- unless there is spooky recursion at a distance going on, there's no reason to mock a method call that you are going to be making in the test itself.
// When
BehaviorSubject.createDefault(false).subscribe(testSubject);
the time I spent figuring out how to mock the LocationManager was big enough to (I think) break one of the common rules in TDD -- a test implementation should not consume too much time.
Right - your current design is fighting with you when you try to test it. That's a symptom; your job as the designer is to identify the problem.
In this case, the code you are trying to test it too tightly coupled to the LocationManager. It is common to create an interface/contract that you can hide a specific implementation behind. Sometimes this pattern is called a seam.
LocationManager::isProviderEnabled, from the outside, is just a function that takes a String and returns a boolean. So instead of writing your method in terms of the LocationManager, write it in terms of the capability that it will give you:
class LocationUtil(isProviderEnabled: (String) -> boolean ) : LocationDataSource {
private var isAvailableSubject: BehaviorSubject<Boolean> =
BehaviorSubject.createDefault(isProviderEnabled(provider))
override fun isAvailable(): Observable<Boolean> = locationSubject
}
In effect, we're trying to push the "hard to test" bits closer to the boundaries, where we'll rely on other techniques to address the risks.

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