Mock an object in Espresso - android

Today I started to use Espresso to test an Activity, I am practical with Junit tests but do not understand how to mock, I have the line cashPeriod that obviously gives me a NPE when I try to launch the activity with Espresso
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//no layout preview possible
setContentView(R.layout.activity_combi_book_balance)
title = getString(R.string.balance_details)
setupActionBarWithHomeEnabled(true)
val cashPeriod: CashPeriod = intent.extras.getParcelable(PERIOD_OBJECT_EXTRA)
val sections = showList(cashPeriod)
setAdapter(sections)
}
and my Espresso test is
#Test
fun appLaunchesSuccessfully() {
ActivityScenario.launch(CombiBookBalanceDetailActivity::class.java)
}
Now if I stub the cashPeriod in the onCreate giving some hardcoded values, the test passes, but I need to do that in the test class of course
How can I mock the line val cashPeriod: CashPeriod = intent.extras.getParcelable(PERIOD_OBJECT_EXTRA) in my espresso test? Is different by Junit, where I use Mockito/mockito-kotlin/mockk and give a behaviour with when /// return //that

You have to use ActivityTestRule
See this

A bit late, but maybe this is what you were looking for. You can add this inside your espresso test.
#Test fun testName() {
val intent = Intent(ApplicationProvider.getApplicationContext(), CombiBookBalanceDetailActivity::class.java)
intent.putExtra(PERIOD_OBJECT_EXTRA, "some test value")
val scenario = launchActivity<CombiBookBalanceDetailActivity>(intent)
}

Related

Android Mockito assertEquals with mocked object

I am writing unit tests and I just can't still find place to use Mockito. For example I have test where I use MockWebServer to mock response. Here is some code, will give you only code of 1 test not to bother you with whole before/after preparation etc..:
#Test
fun `check successful response`() {
val configurationResponse = Mockito.mock(ConfigurationResponse::class.java)
val jsonInString: String = Gson().toJson(configurationResponse)
val response = MockResponse()
.setBody(jsonInString)
.setResponseCode(HttpURLConnection.HTTP_OK)
mockWebServer.enqueue(response)
runBlocking {
val responseResult = restService.getConfiguration()
assertTrue(responseResult is ResultWrapper.Success)
responseResult.getResult(
success = {
assertEquals(
it,
configurationResponse
) //THIS ASSERT GIVES FALSE
}
)
}
}
As you can see I wrote the problem. My assert gives me false. I found out that I can not assert/compare mocked objects with other ones, since it will always give me false, right?
Is there any workaround to accomplish this, it looks very powerful to me if it is doable.

Android - Testing Fragments With Espresso by Using launchFragmentInContainer Never Completes

My test is never running to completion and I have absolutely no idea why. I can see the toast displayed on my phone's screen. There is absolutely nothing in the logs.
#RunWith(AndroidJUnit4::class)
#SmallTest
class BaseDataFragmentUITest
{
#Test
fun isDisplayingToastWhenFAILED_TO_UPDATE()
{
val fragmentScenario = launchFragmentInContainer<TestBaseDataFragmentImp>()
val toastString: String = context.resources.getString(com.developerkurt.gamedatabase.R.string.data_update_fail)
fragmentScenario.onFragment {
it.handleDataStateChange(BaseRepository.DataState.FAILED_TO_UPDATE)
onView(withText(toastString)).inRoot(withDecorView(not(it.requireActivity().getWindow().getDecorView()))).check(matches(isDisplayed()))
}
}
}
Apparently, Espresso assertions shouldn't be made inside of the onFragment block. So when I wrote the test like this it worked:
#Test
fun isDisplayingToastWhenFAILED_TO_UPDATE()
{
val fragmentScenario = launchFragmentInContainer<TestBaseDataFragmentImp>()
val toastString: String = context.resources.getString(com.developerkurt.gamedatabase.R.string.data_update_fail)
var decorView: View? = null
fragmentScenario.onFragment {
it.handleDataStateChange(BaseRepository.DataState.FAILED_TO_UPDATE)
decorView = it.requireActivity().getWindow().getDecorView()
}
onView(withText(toastString)).inRoot(withDecorView(not(decorView!!))).check(matches(isDisplayed()))
}

Android Instrumented testing asserting if an activity is started

I am developing an Android application using Kotlin programming language. I am adding instrumentation tests into my application. Now I am trying to test if an activity is started after some delay.
This is my activity code.
class MainActivity : AppCompatActivity() {
companion object {
val LAUNCH_DELAY: Long = 2000
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Handler().postDelayed({
this.startLoginActivity()
}, LAUNCH_DELAY)
}
protected fun startLoginActivity()
{
startActivity(Intent(this, LoginActivity::class.java))
}
}
I know how to write a simple test like this
#Test
fun itRendersCompanyName() {
onView(withId(R.id.main_tv_company_name)).check(matches(withText("App Name")))
}
But what I am trying to test here is if the LoginActivity is launched after some delay. How can I do it using Espresso framework?
You can get the visible Activity using ActivityManager:
inline fun <reified T : Activity> isVisible(): Boolean {
val am = ApplicationProvider.getApplicationContext<Context>().getSystemService(ACTIVITY_SERVICE)
as ActivityManager
val visibleActivityName = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
am.appTasks[0].taskInfo.topActivity.className
} else {
am.getRunningTasks(1)[0].topActivity.className
}
return visibleActivityName == T::class.java.name
}
Calling isVisible<LoginActivity>() will tell you that LoginActivity is visible or not.
Also, to wait until your LoginActivity visible, you can wait for this method to gets true. For example:
inline fun <reified T : Activity> waitUntilActivityVisible() {
val startTime = System.currentTimeMillis()
while (!isVisible<T>()) {
Thread.sleep(200)
if (System.currentTimeMillis() - startTime >= TIMEOUT) {
throw AssertionError("Condition unsatisfied after $TIMEOUT milliseconds")
}
}
}
You can use Intents.intended() for that.
Add following to your build.gradle file:
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
In your test function, you can try following code:
Intents.init()
Intents.intended(hasComponent(LoginActivity::class.java!!.getName()))
You can read more about Espresso-Intents here.
It’s better to test this state with unit tests. Use architecture pattern (for example MVP/MVVM), mock presenter/view model and check what method which is responsible for activity start is triggered
I got it working using the following:
val expectedUrl = "https://yoururlhere"
Intents.init();
Matcher<Intent> expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(expectedUrl));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withId(R.id.someViewId)).perform(click());
intended(expectedIntent);
Intents.release();
And remember to add "androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'" to your gradle dependency

Mockito.verify didn't see method exectution, even if it was

The error I have:
The code with the error:
#RunWith(PowerMockRunner::class)
#PrepareForTest(PotatoProvider::class, PotatoConsumer::class)
class WantedButNotInvoked {
#Mock
lateinit var potatoConsumer: PotatoConsumer
#Test
fun potato() {
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
//verify(potatoConsumer).accept(any()) //-> This fails too with the same reason
}
}
data class Potato(val value: Int = 1)
class PotatoConsumer : Consumer<Potato> {
override fun accept(t: Potato?) {
println(t)
}
}
So I making subscribe with this mock(potatoConsumer), and the rxJava have called 'accept', and mockito mark it as interaction, but mockito thinks this interaction is not what I'm expecting, why?
Versions of libraries used her:
mockitoVersion = '2.8.9'
mockitoAndroidVersion = '2.7.22'
powerMockVersion="2.0.2"
kotlinMockito="2.1.0"
rxKotlin = "2.3.0"
rxJavaVersion = "2.2.10"
Kinda workaround
Some fields mocked by powermock, fails on 'verify', but fields mocked with mockito is not;
Mockito can't mock not opened fields, without mock-maker-inline, but mockito conflicts with Powermock mock-maker-inline;
Powermock can delegate calls of mock-maker-inline to other mock-maker-inline(https://github.com/powermock/powermock/wiki/PowerMock-Configuration)
Use Mockito.mock on the failed fields instead of #Mock/Powermock mock injection
Example of the "green" potato test method using PowerMockRunner
#Test
fun potato() {
potatoConsumer = mock() // <-
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(potato)
}
I am not familiar with PowerMock but I tried this test and it passes:
#Test
fun potato() {
fakePotatoProvider = Mockito.mock(PotatoProvider::class.java)
potatoConsumer = Mockito.mock(PotatoConsumer::class.java)
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(Potato()))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
}
Maybe because you aren't passing the same instance of Potato(). Try to refactor your code to this
#Test
fun potato() {
val testPotato = Potato()
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(testPotato))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(testPotato)
}
As I mentioned above, the reason why it might be failing is the constant creation of new instances when passing your Potato object, hance that comparison fails.

How to test constraints on object initialization?

I am writing an App for Android, and I am wanting to start writing tests for the classes I am writing. I am fairly new to writing test cases.
Right now to create a test, I use IntelliJ and use it's wizard to make a new JUnit4 test. The wizard allows me to select methods from my class to test.
But for the object I am testing, I do not want a negative number passed to the constructor.
class MinuteTime(private val minutes : Int) {
init {
if (minutes < 0) {
throw IllegalArgumentException("Cannot be less than 0.")
}
}
...
Where in my Test class is the best place to test these constraints? I know that to test the constraints, I just need to make sure the exception is thrown and caught, but I am unsure if I should make a new method for this, or just wedge it into one of the functions IntelliJ pre-made for me. Here is the generated test class:
class MinuteTimeTest {
#Test
fun getTimeInMinutes() {
}
#Test
fun getHours() {
}
#Test
fun getMinutes() {
}
#Test
fun plus() {
}
}
I'd definetly recommend to wrap this into a separate test method. Its concern is simply testing the validation during initialization:
#Test
fun negativeNumberConstructorTest() {
assertFailsWith(IllegalArgumentException::class){
MinuteTime(-1)
}
}
It’s using kotlintest on top of JUnit

Categories

Resources