Gradle sets the build to failed when unit test failed, but set BUILD SUCCESSFUL when instrumented test fails(espresso in my case) anybody know how to force build to fail when espresso failed?
Ops: need this to integrate with Jenkins, so it must failed when unit test and espresso test failed and don't want to use Jenkins Text-finder plugin.
Just to let everyone know, I founded a solution, following the answer:android-gradle-plugin 1.2.2: Flag ignoreFailures causes error in task connectedAndroidTest , I did
project.gradle.taskGraph.whenReady {
connectedAndroidTest[flavor]Debug {
ignoreFailures = false
}
connectedAndroidTest[flavor2]Debug {
ignoreFailures = false
}
}
and execute ./gradlew connectedAndroidTest --continue
now it runs all instrumented test for both flavours and if there is a failure in any one of them, the build failure as well.
I found more clean solution, use in root build.gradle:
//if in start command "--continue" was added, then apply ignoring for android test fails
gradle.taskGraph.whenReady { graph ->
if (gradle.startParameter.continueOnFailure) {
graph.allTasks.findAll { it.name ==~ /connected.*AndroidTest/ }*.ignoreFailures = true
}
}
This solution works for me.
Add this code to UI Test class:
// Use this TestRule to setup IdlingResource
#Rule
#JvmField
val mIdlingResourceTestRule = CustomIdlingResTestRule()
This is code of CustomIdlingResTestRule:
import androidx.test.espresso.IdlingPolicies
import androidx.test.espresso.IdlingRegistry
import com.jakewharton.espresso.OkHttp3IdlingResource
import com.kasikorn.retail.mbanking.kplus.basemodule.AppModule
import com.kasikorn.retail.mbanking.kplus.util.OkHttpProvider
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import java.util.concurrent.TimeUnit
/**
* TestRule class help to setup IdlingResource
*/
class CustomIdlingResTestRule : TestRule {
protected var mIdlingResource: CustomIdlingResource = CustomIdlingResource()
val idlingResourceOkHttp: OkHttp3IdlingResource by lazy {
OkHttp3IdlingResource.create(AppModule.APP_CONTEXT, OkHttpProvider.getOkHttpInstance())
}
override fun apply(base: Statement?, description: Description?): Statement {
return object : Statement() {
override fun evaluate() {
IdlingPolicies.setMasterPolicyTimeout(60, TimeUnit.SECONDS)
IdlingPolicies.setIdlingResourceTimeout(26, TimeUnit.SECONDS)
IdlingRegistry.getInstance().register(idlingResourceOkHttp, mIdlingResource)
//NOTE: use try cache to ignore UI Test Failures
try {
base?.evaluate()
} catch (e: Throwable) {
e.printStackTrace()
}
IdlingRegistry.getInstance().unregister(idlingResourceOkHttp, mIdlingResource)
}
}
}
}
import android.app.Activity
import android.util.Log
import androidx.test.espresso.IdlingResource
import androidx.test.espresso.core.internal.deps.guava.collect.Iterables
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage
class CustomIdlingResource() : IdlingResource {
private var mIsIdle = false
private var resourceCallback: IdlingResource.ResourceCallback? = null
override fun getName(): String = this.javaClass.simpleName
override fun isIdleNow(): Boolean {
if (mIsIdle) return true
val currentActivity = getCurrentActivity()
Log.d("LOG", this.javaClass.simpleName + " | isIdleNow() | currentActivity: $currentActivity")
if (currentActivity != null) {
mIsIdle = true
resourceCallback?.onTransitionToIdle()
}
return mIsIdle
}
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
this.resourceCallback = callback
}
private fun getCurrentActivity(): Activity? {
val activity = arrayOfNulls<Activity>(1)
val activities: Collection<Activity> = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED)
activity[0] = Iterables.getOnlyElement(activities)
return activity[0]
}
}
Related
Maybe this is very basic question but could not find anything online.
I have created a object class in kotlin contains few methods. I am calling those from ViewModel and I have written junit test case for ViewModel where object class instance is mocked, fine so far.
Now, I want to write junit for my object class separately as well even though from ViewModel verify() calls are working fine.
Some code snippet from my project
object InfoHelper {
fun method(param: Xyz): Boolean {
return when(param) {
Result.OK -> true
else -> false
}
}
}
Unit testing Kotlin object classes:
The same as testing a Java static method and class. The class under test and it's methods can be tested directly without initialisation.
Using your class as a loose example..
object InfoHelper {
fun method(param: String): Boolean {
return when(param) {
"my string" -> true
else -> false
}
}
}
The test:
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class InfoHelperTest {
#Test
fun `some test returns true`() {
assertTrue(InfoHelper.method("my string"))
}
#Test
fun `some test returns false`() {
assertFalse(InfoHelper.method("not my string"))
}
}
I am new on flutter and I want change wallpaper of the device but that needs Method calls from platform channel
https://developer.android.com/reference/android/Manifest.permission?hl=en#SET_WALLPAPER
and
native android wallpaperManager
under the android folder /MainActivity.kt Kotlin file throws an error.
I tried these below:
A new fresh project with MainActivity.java file
flutter clean
flutter pub cache repair
I read about other type mismatch error questions from here but unfortunately not found anything.
Any help appriciated.
My code is as below.
MainActivity.kt
package com.combasis.wallpaper_son_app
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import java.io.IOException
import android.app.WallpaperManager
import android.graphics.BitmapFactory
import java.io.File
import android.os.Build
import android.annotation.TargetApi
import android.content.Context
import io.flutter.Log
private const val CHANNEL = "com.combasis.wallpaper_son_app"
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "setWallpaper") {
val arguments = call.arguments as ArrayList<*>
val setWallpaper = setWallpaper(arguments[0] as String, applicationContext, arguments[1] as Int)
if (setWallpaper == 0) {
result.success(setWallpaper)
} else {
result.error("UNAVAILABLE", "", null)
}
} else {
result.notImplemented()
}
}
}
#TargetApi(Build.VERSION_CODES.ECLAIR)
private fun setWallpaper(path: String, applicationContext: Context, wallpaperType: Int): Int {
var setWallpaper = 1
val bitmap = BitmapFactory.decodeFile(path)
val wm: WallpaperManager? = WallpaperManager.getInstance(applicationContext)
setWallpaper = try {
wm?.setBitmap(bitmap, null, true, wallpaperType)
0
} catch (e: IOException) {
1
}
return setWallpaper
}
}
main.dart:...
static const platform = const MethodChannel('com.combasis.wallpaper_son_app');
Future<void> _setWallpaper(int wallpaperType, String url) async {
var file =
await DefaultCacheManager().getSingleFile(url);
try {
final int result = await platform
.invokeMethod('setWallpaper', [file.path, wallpaperType]);
print('Wallpaper Updated.... $result');
} on PlatformException catch (e) {
print("Failed to Set Wallpaper: '${e.message}'.");
}
}
Run:
Launching lib/main.dart on AOSP on IA Emulator in debug mode...
Running Gradle task 'assembleDebug'...
e: /Users/username/AndroidStudioProjects/walpaperz_app/wallpaper_son_app/android/app/src/main/kotlin/com/combasis/wallpaper_son_app/MainActivity.kt: (21, 48): Type mismatch: inferred type is MainActivity but FlutterEngine was expected
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileDebugKotlin'.
> Compilation error. See log for more details
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 13s
Exception: Gradle task assembleDebug failed with exit code 1
-Android Studio version: 4.0.1
-Kotlin version: 1.3.72-release-Studio4.0-5 stable
-Flutter version: 1.17.2 stable
Please remove GeneratedPluginRegistrant.registerWith(flutterEngine); from MainActivity.kt and update as follows.
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
To pass flutter Engine value we can use provideFlutterEngine method. And for flutterView we can use flutterEngine.dartExecutor. Below is the code snippet.
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.startActivity/testChannel"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
provideFlutterEngine(this)?.let { GeneratedPluginRegistrant.registerWith(it) }
MethodChannel(flutterEngine?.dartExecutor,CHANNEL).setMethodCallHandler{ call, result ->
if(call.method.equals("StartSecondActivity")){
val intent= Intent(this,KotlinActivity::class.java)
startActivity(intent)
result.success("ActivityStarted")
}
else{
result.notImplemented()
}
}
}}
You could create a new app using flutter create or via IDE wizard, the MainActivity.kt may be as follows:
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}
then start work from it.
I want to try out work manager for the first time. I am used to rxJava so I decided to implement my work manager using RxWorker. But the testing aspect is giving me headache.Basically, the work manager checks firebase to get latest changes to latest changes to particular document (This is not the best use case I know).But the problem is in the test returns without waiting for success or failure.It returns when the work manager is still running.
This is my work manager implementation
class MidiSyncWorker(context: Context, params: WorkerParameters) : RxWorker(context, params) {
override fun createWork(): Single<Result> {
return Injection.provideSharePrefsRepo.midiArchiveVersion()
.flatMapObservable { currentVersion ->
Injection.provideOnlineRepo.latestMidiArchive()
.filter { onlineMidi -> onlineMidi.version > currentVersion }
}.firstOrError()
.map { onlineMidi ->
val outputData = Data.Builder()
.putString(KEY_FIREBASE_ARCHIVE_PATH, onlineMidi.url)
Result.success(outputData.build()) }
}
.onErrorReturn { Result.failure() }
}
This is my test
fun midiSyncVersionCheck_success_onlineVersionDiffersFromLocalVersion() {
// create request
val request = OneTimeWorkRequestBuilder<MidiSyncWorker>()
.build()
wmRule.workManager.enqueue(request).result.get()
val workInfo = wmRule.workManager.getWorkInfoById(request.id).get(10, TimeUnit.SECONDS)
assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
}
I expected the test to wait until workmanager returns success or failure. But it returns while work manager is still running
java.lang.AssertionError:
Expected: is <SUCCEEDED>
but: was <RUNNING>
WorkManager makes available a couple of ways to test your Worker classes. You can find all the details on WorkManager Testing documentation page.
The original WorkManagerTestInitHelper only supports Worker classes, meanwhile, the newly introduce in (WorkManager v2.1.0-alpha01) TestListenableWorkerBuilder can be used to test both ListenableWorker classes and the other classes that derives from it (like CoroutineWorker and RxWorker.
In your particular case, you should be able to do:
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker.Result
import androidx.work.testing.TestListenableWorkerBuilder
import org.hamcrest.CoreMatchers.`is`
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
#RunWith(JUnit4::class)
class MyWorkTest {
private lateinit var context: Context
#Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
}
#Test
fun testMidiSyncWorker() {
// Get the ListenableWorker
val worker = TestListenableWorkerBuilder<MidiSyncWorker>(context).build()
// Start the work synchronously
val result = worker.startWork().get()
assertThat(result, `is`(Result.success()))
}
}
In this way you're calling synchrously your worker class.
In this case you need to use the as a test dependency in your build.gradle file:
def work_version = "2.1.0-alpha02"
androidTestImplementation "androidx.work:work-testing:$work_version"
You can find a similar, complete, sample (for a CoroutineWorker), on the Kotlin's coroutine Codelab.
Test cases are running well individually but when I run all test cases together, some of them failed.
I tried to use Mockito.reset function to reset the mocks but it is not working
So, what can I do to reset the mocks ?
note: I'm using mockito 2.21.0
class MainPresenterTest {
private lateinit var api: Api
private lateinit var mainViewContract: MainActivity
private lateinit var presenter: MainPresenter
#Before
fun setup() {
api = Mockito.mock(Api::class.java)
mainViewContract = Mockito.mock(MainActivity::class.java)
presenter = MainPresenter(mainViewContract, api)
}
#After
fun resetMocks() {
reset(api, mainViewContract)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().build())
}
#Test
fun changeShiftToOn1() {
Shared.shiftState = ShiftStatus.OFF
val postShiftResponse = PostShiftResponse(200, "132")
Mockito.`when`(api.startShift()).thenReturn(postShiftResponse)
Mockito.`when`(mainViewContract.popupErrorMessage()).then { }
Mockito.`when`(mainViewContract.showShiftDialog()).then { }
Mockito.`when`(mainViewContract.dismissDialog()).then { }
Mockito.`when`(mainViewContract.showProgressDialog()).then { }
presenter.changeShiftState()
Mockito.verify(mainViewContract, Mockito.atLeastOnce()).showConnectingBar()
Mockito.verify(mainViewContract, Mockito.atLeastOnce()).startServices()
Mockito.verify(mainViewContract, Mockito.atLeastOnce()).setStartShiftLayout(false)
}
#Test
fun changeShiftToOn2() {
Shared.shiftState = ShiftStatus.OFF
val postShiftResponse = PostShiftResponse(400, "132")
Mockito.`when`(api.startShift()).thenReturn(postShiftResponse)
Mockito.`when`(mainViewContract.popupErrorMessage(null)).then { }
Mockito.`when`(mainViewContract.showShiftDialog()).then { }
Mockito.`when`(mainViewContract.dismissDialog()).then { }
Mockito.`when`(mainViewContract.showProgressDialog()).then { }
presenter.changeShiftState()
Mockito.verify(mainViewContract, Mockito.atLeastOnce()).popupErrorMessage(null)
Mockito.verify(mainViewContract, Mockito.atLeastOnce()).showShiftDialog()
}
}
If you are doing some asynchronous tasks, then ensure that next instruction is executed after async operation is completed.
In order to wait for async tasks there is a library called Awaitility.
Here is it's link;
https://github.com/awaitility/awaitility.git
You can also use it as I've,
//SUT is System Under Test
SUT.login()
await().until {
SUT.coroutineContext.isActive
}
I try to mock some methods in the project so that when they are called, a certain value is returned.
But when you run the tests, they fall with the output:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers! 0 matchers expected, 1 recorded:
-> at com.hodzi.stackviewer.questions.detail.QuestionDetailPresenterTest.voteTest(QuestionDetailPresenterTest.kt:69)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String"); When using matchers, all arguments have to be provided by matchers. For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
If you run the same code in debug mode and run through all the lines, then when you call shared.getToken (), the value that we specified is returned. But with normal startup, tests fall on this line.
Code:
import com.hodzi.stackviewer.questions.QuestionsInteractor
import com.hodzi.stackviewer.utils.Shared
import com.hodzi.stackviewer.utils.Vote
import org.junit.BeforeClass
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
internal class QuestionDetailPresenterTest {
companion object {
lateinit var presenter: QuestionDetailPresenter
lateinit var view: QuestionDetailView
#BeforeClass #JvmStatic
fun setUp() {
val questionsInteractor: QuestionsInteractor =
Mockito.mock(QuestionsInteractor::class.java)
val shared: Shared =
Mockito.mock(Shared::class.java)
Mockito.`when`(shared.getToken()).thenReturn("23")
// Mockito.doReturn("23").`when`(shared).getToken()
view = Mockito.mock(QuestionDetailView::class.java)
presenter = QuestionDetailPresenter(questionsInteractor, shared)
}
}
#Test
fun voteTest() {
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Mockito.verify(view).goToAuth()
}
}
Shared:
interface Shared {
companion object {
const val KEY_TOKEN: String = "keyToken"
}
fun getToken(): String
fun saveToken(token: String?)
}
Presenter:
class QuestionDetailPresenter(val questionsInteractor: QuestionsInteractor, val shared: Shared) :
BasePresenter<QuestionDetailView>() {
lateinit var question: Question
fun vote(id: Int, vote: Vote) {
print(vote)
if (Strings.isEmptyString(shared.getToken())) {
view?.goToAuth()
return
}
val observable: Observable<out Data> = when (vote) {
Vote.ANSWER_UP -> {
questionsInteractor.answerUpVote(id, shared.getToken())
}
Vote.ANSWER_DOWN -> {
questionsInteractor.answerDownVote(id, shared.getToken())
}
Vote.QUESTION_UP -> {
questionsInteractor.questionUpVote(id, shared.getToken())
}
Vote.QUESTION_DOWN -> {
questionsInteractor.questionDownVote(id, shared.getToken())
}
}
baseObservableData(observable,
{ data ->
run {
Log.d(Const.LOG_TAG, "success")
}
},
{ throwable ->
run {
Log.d(Const.LOG_TAG, "error")
}
}
)
}
}
Thanks!
There's nothing wrong with your mocking of shared, I think the problem is with:
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Just use a real Int instead of the ArgumentMatchers.anyInt().
Like
presenter.vote(0, Vote.QUESTION_DOWN)
Matchers are used when matching arguments on a mocked object, for example
val calulator = (mock with Mockito)
when(calculator.divideByTwo(anyInt()).thenReturn(1)
would mean calculator.divideByTwo(int: Int) returns 1 when called with any Int.
When calling methods of real objects to test them (like you do with your presenter), you use real parameters.