changing test cases from junit to mockito in android app - android

i am trying to test out an email field in login screen. i want to change the following code using mockito. can it be done. pls help??
class EmailValidatorTest {
#Test
fun emailValidator_CorrectEmailSimple_ReturnsGood() {
val email = "name#email.com"
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.GOOD)
}
#Test
fun emailValidator_CorrectEmailSubDomain_ReturnsGood() {
val email = "name#email.co.uk"
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.GOOD)
}
#Test
fun emailValidator_InvalidEmailNoTld_ReturnsIncorrect() {
val email = "name#email"
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.INCORRECT)
}
#Test
fun emailValidator_InvalidEmailDoubleDot_ReturnsIncorrect() {
val email = "name#email..com"
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.INCORRECT)
}
#Test
fun emailValidator_InvalidEmailNoUsername_ReturnsIncorrect() {
val email = "#email.com"
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.INCORRECT)
}
#Test
fun emailValidator_EmptyString_ReturnsEmpty() {
val email = ""
assertThat(EmailPasswordChecker.getEmailState(email)).isEqualTo(EmailState.EMPTY)
}
here is some more helper code->
object EmailPasswordChecker{
//some code that i cant share
}
enum class EmailState {
GOOD,
INCORRECT,
EMPTY
}
these are simple tests that i wrote using JUnit. but now i need to change them using mockito. can someone help me with the code? i am fairly new to testing so i am not exactly able to figure out how this framework is working.

Related

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()))
}

Testing Android Room with Kotlin Flow

I'm trying to test a Room DAO exposing functions that return Flows. The following test won't pass and I'm struggling to see why :
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
val outputList: MutableList<List<HomeCourse>> = mutableListOf()
launch { subject.observeHomeCoursesFeatured().collect { outputList.add(it) } }
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
assertEquals(2, outputList.size)
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), outputList[0])
assertEquals(listOf(getHomeCourseFeatured1()), outputList[1])
}
It fails at assertEquals(2, outputList.size) saying that outputList is empty.
This test passes :
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), subject.observeHomeCoursesFeatured().first())
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
assertEquals(listOf(getHomeCourseFeatured1()), subject.observeHomeCoursesFeatured().first())
}
The second test passing, shows that my DAO is working fine and it is more a question of threading and concurrency between the test thread and the thread that Room uses to trigger Flow changes.
I already added #get:Rule val archRule = InstantTaskExecutorRule() in my test. I also build my test DB with this :
db = Room.inMemoryDatabaseBuilder(ctx, CoreDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor())
.allowMainThreadQueries()
.build()
What am I missing ?
launch is asynchronous, so you have a race condition.
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
val job = async { subject.observeHomeCoursesFeatured().take(2).toList() }
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
val outputList = job.await()
assertEquals(2, outputList.size)
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), outputList[0])
assertEquals(listOf(getHomeCourseFeatured1()), outputList[1])
}

Mockk - MockKException when testing password against regex

I've just started to do Unit testing in Kotlin using Mockk.
I'm trying to test the following function:
fun evaluatePredicate(regEx: String, passwordInserted: String) : Boolean {
return passwordInserted.matches(regEx.toRegex())
}
My test look like this:
#Test
fun evaluatePredicate_shouldContainLowerCase_trueExpected() {
//given
val regEx = ".*[a-z]+.*" //lower case
val password = "password"
every { password.matches(regEx.toRegex()) } returns true
every { SUT.evaluatePredicate(regEx, password) } returns true
//when
val evaluate = password.matches(regEx.toRegex())
val result = SUT.evaluatePredicate(regEx, password)
//then
assertEquals(evaluate, result)
}
But I'm getting :
io.mockk.MockKException: Missing calls inside every { ... } block.
at line:
every { password.matches(regEx.toRegex()) } returns true
I've tried to use Mockk Matcher any() instead of matches(regEx.toRegex()) but nothing changed.
I'm not sure if I'm using the right tools for the job here.
Any suggestion is welcome.

How to take screenshots and send them by e-mail as part of a Continuous Integration process?

I'm developing an Android Library Module which is highly customizable in terms of UI. It would be really nice to have a script or some sort of automated process that takes screen shots of the running app, concatenate them and send by e-mail - so then I could quickly check if some change has messed with some UI component and/or have the most recent assets to update library READ-ME.
Any idea on how this could be performed?
My current idea
So far I've thought in adding code to programmatically take SS, store them on a temporary folder and, when all images has been collected, send them via some REST API to a server. I'd like to know if there is a better way to do that.
I ended up following my initial idea:
Based on this answer I've implemented a method that takes screenshots;
Base on this answer, I've implemented the API JavaMail capable of sending e-mails without the need of user interaction;
The combination of 1 and 2 can be found on my util library kotlin-components
Finally I've implemented UI tests that enters the desired state, takes the screen shots - saving them on external SD card - and, on the last step, it adds the SS as e-mail attachments sending to whatever I want to:
#RunWith(AndroidJUnit4::class)
#LargeTest
class UITestSearchSamples {
companion object {
private val SCREENSHOTS_DIRECTORY = "search-interface"
private val TIME_OUT = 3000L
private val WAITING_TIME = 1000L
#get:ClassRule
var disableAnimationsRule = DisableAnimationsRule()
}
private var finished = false
#get:Rule
var mActivityRule = ActivityTestRule(ActivityHomepage::class.java)
private var mMonitor: Instrumentation.ActivityMonitor? = null
#Before
fun setup() {
setWaitingPolice()
mMonitor = getInstrumentation().addMonitor(ActivitySearch::class.java.name, null, false)
}
private fun performWaitingTime() {
val idlingResource = ElapsedTimeIdlingResource(WAITING_TIME)
Espresso.registerIdlingResources(idlingResource)
}
private fun setWaitingPolice() {
IdlingPolicies.setMasterPolicyTimeout(TIME_OUT, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(TIME_OUT, TimeUnit.MILLISECONDS);
}
#After
fun tearDown() {
closeSoftKeyboard()
performWaitingTime()
val activitySearch = getInstrumentation().waitForMonitorWithTimeout(mMonitor, TIME_OUT) as AppCompatActivity
activitySearch.takeScreenShot(location = DirectoryPath.EXTERNAL, path = SCREENSHOTS_DIRECTORY, openScreenShot = false, showToast = false)
activitySearch.finish()
if (finished) {
sendNotificationEmail(activitySearch)
}
}
private fun sendNotificationEmail(activitySearch: AppCompatActivity) {
try {
val sender = Sender("sender_email", "sender_password")
val email = Email(
"Hello world: SMTP Server from Android with Attachments",
"This is a sample e-mail sent via SMTP server from Android without the need of user interaction.",
mutableListOf("recipient_01", "recipient_02"),
File("${DirectoryPath.EXTERNAL.getValue(activitySearch)}/search-interface").listFiles()
)
activitySearch.sendEmail(sender, email)
} catch (e: Exception) {
Log.e("SENDER E-MAIL SLAVE", e.message, e)
}
}
#Test
fun launchSample01() {
onView(withId(R.id.btn_sample_01)).perform(click())
onView(withId(R.id.input)).perform(typeText("Diana"))
}
#Test
fun launchSample02() {
onView(withId(R.id.btn_sample_02)).perform(click())
onView(withId(R.id.input)).perform(typeText("Clark"))
}
#Test
fun launchSample03() {
onView(withId(R.id.btn_sample_03)).perform(click())
onView(withId(R.id.input)).perform(typeText("Diana"))
onView(withId(R.id.wrapper)).perform(click())
performWaitingTime()
onView(withId(R.id.input)).perform(typeText("a"))
finished = true
}
}

Mockito Wanted but not Invoked

I'm new to writing tests and using Mockito.
I've read the similar topics here on Stackoverflow and made the suggested changes, making sure that regarded classes / interfaces / methods are open.
I tried to follow this
Mocking the constructor injected dependencies
This is the test I came up with so far
class RegistrationPresenterTest {
#Test
fun testRegisterSuccess() {
val mockService = mock<IHerokuInteractor>()
val mockLocal = mock<ILocalStorageInteractor>()
val mockView = mock<RegisterView>()
val mockRegistrationResponse = HerokuRegisterResponse("hash")
val mockPair = ImeiPair("imei","hash")
val presenter = RegisterPresenterImpl(mockLocal,mockService)
whenever(mockService.register(any())).thenReturn(Observable.just(mockRegistrationResponse))
whenever(mockLocal.clearPreferences()).thenReturn(Observable.just(true))
whenever(mockLocal.putImeiPair(any())).thenReturn(Observable.just(true))
//whenever(presenter.writeImeiPairLocally(any())) How do I specify parameters since it uses a parameter from the register method?
presenter.bindView(mockView)
presenter.register("imei","male")
verify(mockService, times(1)).register(any())
verify(mockLocal,times(1)).clearPreferences()
verify(mockLocal,times(1)).putImeiPair(any())
verify(mockView,times(1)).moveToMain()
}
but the response I keep getting is
Wanted but not invoked:
registerPresenterImpl.writeImeiPairLocally(
<any com.company.appname.model.ImeiPair>
);
Actually, there were zero interactions with this mock.
I got this response even when I don't mention that method in the test.
This is my presenter register method. I've changed the classes / interfaces & methods involved to open (kotlin). I believe override methods are open by nature in kotlin.
open class RegisterPresenterImpl #Inject constructor(val localStorage : ILocalStorageInteractor, var herokuService : IHerokuInteractor)
override fun register(imei : String, gender : String){
subscription = herokuService.register(RegisterObject(imei,gender)).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(
{
registrationResult ->
Log.d(TAG,"${registrationResult}")
if(registrationResult.imei_hash != null){
writeImeiPairLocally(ImeiPair(imei,registrationResult.imei_hash))
}
else{
Log.e(TAG,"User already exists")
}
},
{
errorResponse -> Log.e(TAG,"Could not register user ${errorResponse.message}")
}
)
addSubscription(subscription)
}
and similarly the
open fun writeImeiPairLocally(pair : ImeiPair){
subscription = localStorage.clearPreferences().flatMap {
cleared -> localStorage.putImeiPair(pair)}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(
{
booleanResult -> view?.moveToMain()
},
{
errorResponse -> Log.e(TAG,"Could not write ImeiPair to SharedPreferences ${errorResponse.message}")
}
)
addSubscription(subscription)
}
Here is interfaces
open interface ILocalStorageInteractor : ILocalStorage{
fun getImeiPair() : Observable<ImeiPair>
fun putImeiPair(pair: ImeiPair) : Observable<Boolean>
}
open interface ILocalStorage {
fun clearPreferences() : Observable<Boolean>
}
All help is appreciated.
If you are using plain jUnit, then your AndroidSchedulers.mainThread() is null. That's why onNext is not called.
You need to override Schedulers in a setUp() method with:
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
#Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate(); // or .test()
}
});
To avoid concurrency in tests, I would recommend to override Schedulers.io() like this:
RxJavaHooks.setOnIOScheduler(scheduler1 -> Schedulers.immediate());
If you are going to use TestScheduler, don't forget to call TestScheduler.triggerActions() method.
Also don't forget to unregister Schedulers in tearDown() like this:
RxJavaHooks.reset();
RxAndroidPlugins.getInstance().reset();
AndroidSchedulers.reset();
Schedulers.reset();

Categories

Resources