Checking toast message in android espresso - android

Would anyone know how to test for the appearance of a Toast message in android espresso? In robotium its easy & I used but started working in espresso but dont getting the exact command.

This slightly long statement works for me:
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
....
onView(withText(R.string.TOAST_STRING)).inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView())))).check(matches(isDisplayed()));

The accepted answer is a good one but didn't work for me. So I searched a bit and found this blog article.
This gave me an idea of how to do it and I updated the solution above.
First I implemented the ToastMatcher:
import android.os.IBinder;
import android.support.test.espresso.Root;
import android.view.WindowManager;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class ToastMatcher extends TypeSafeMatcher<Root> {
#Override
public void describeTo(Description description) {
description.appendText("is toast");
}
#Override
public boolean matchesSafely(Root root) {
int type = root.getWindowLayoutParams().get().type;
if (type == WindowManager.LayoutParams.TYPE_TOAST) {
IBinder windowToken = root.getDecorView().getWindowToken();
IBinder appToken = root.getDecorView().getApplicationWindowToken();
if (windowToken == appToken) {
// windowToken == appToken means this window isn't contained by any other windows.
// if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
return true;
}
}
return false;
}
}
Then I implemented my check methods like this:
public void isToastMessageDisplayed(int textId) {
onView(withText(textId)).inRoot(MobileViewMatchers.isToast()).check(matches(isDisplayed()));
}
MobileViewMatchers is a container for accessing the matchers. There I defined the static method isToast().
public static Matcher<Root> isToast() {
return new ToastMatcher();
}
This works like a charm for me.

First make sure to import:
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
Inside your class you probably have a rule like this:
#Rule
public ActivityTestRule<MyNameActivity> activityTestRule =
new ActivityTestRule<>(MyNameActivity.class);
Inside your test:
MyNameActivity activity = activityTestRule.getActivity();
onView(withText(R.string.toast_text)).
inRoot(withDecorView(not(is(activity.getWindow().getDecorView())))).
check(matches(isDisplayed()));
This worked for me, and it was pretty easy to use.

If you're using the newest Android Testing Tools from Jetpack, you know, that ActivityTestRule is deprecated and you should use ActivityScenario or ActivityScenarioRule(which contains the first).
Prerequisites. Create decorView variable and assign it before tests;
#Rule
public ActivityScenarioRule<FeedActivity> activityScenarioRule = new ActivityScenarioRule<>(FeedActivity.class);
private View decorView;
#Before
public void setUp() {
activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction<FeedActivity>() {
#Override
public void perform(FeedActivityactivity activity) {
decorView = activity.getWindow().getDecorView();
}
});
}
Test itself
#Test
public void given_when_thenShouldShowToast() {
String expectedWarning = getApplicationContext().getString(R.string.error_empty_list);
onView(withId(R.id.button))
.perform(click());
onView(withText(expectedWarning))
.inRoot(withDecorView(not(decorView)))// Here we use decorView
.check(matches(isDisplayed()));
}
getApplicationContext() can be taken from androidx.test.core.app.ApplicationProvider.getApplicationContext;

First create a cutom Toast Matcher which we can use in our test cases -
public class ToastMatcher extends TypeSafeMatcher<Root> {
#Override public void describeTo(Description description) {
description.appendText("is toast");
}
#Override public boolean matchesSafely(Root root) {
int type = root.getWindowLayoutParams().get().type;
if ((type == WindowManager.LayoutParams.TYPE_TOAST)) {
IBinder windowToken = root.getDecorView().getWindowToken();
IBinder appToken = root.getDecorView().getApplicationWindowToken();
if (windowToken == appToken) {
//means this window isn't contained by any other windows.
return true;
}
}
return false;
}
}
1. Test if the Toast Message is Displayed
onView(withText(R.string.mssage)).inRoot(new ToastMatcher())
.check(matches(isDisplayed()));
2. Test if the Toast Message is not Displayed
onView(withText(R.string.mssage)).inRoot(new ToastMatcher())
.check(matches(not(isDisplayed())));
3. Test id the Toast contains specific Text Message
onView(withText(R.string.mssage)).inRoot(new ToastMatcher())
.check(matches(withText("Invalid Name"));
Thanks,
Anuja
Note - this answer is from This POST.

Though the question has an accepted answer - which BTW does not work for me - I'd like to add my solution in Kotlin which I derived from Thomas R.'s answer:
package somepkg
import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.Root
import android.support.test.espresso.matcher.ViewMatchers.withText
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
import android.view.WindowManager.LayoutParams.TYPE_TOAST
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
/**
* This class allows to match Toast messages in tests with Espresso.
*
* Idea taken from: https://stackoverflow.com/a/33387980
*
* Usage in test class:
*
* import somepkg.ToastMatcher.Companion.onToast
*
* // To assert a toast does *not* pop up:
* onToast("text").check(doesNotExist())
* onToast(textId).check(doesNotExist())
*
* // To assert a toast does pop up:
* onToast("text").check(matches(isDisplayed()))
* onToast(textId).check(matches(isDisplayed()))
*/
class ToastMatcher(private val maxFailures: Int = DEFAULT_MAX_FAILURES) : TypeSafeMatcher<Root>() {
/** Restrict number of false results from matchesSafely to avoid endless loop */
private var failures = 0
override fun describeTo(description: Description) {
description.appendText("is toast")
}
public override fun matchesSafely(root: Root): Boolean {
val type = root.windowLayoutParams.get().type
#Suppress("DEPRECATION") // TYPE_TOAST is deprecated in favor of TYPE_APPLICATION_OVERLAY
if (type == TYPE_TOAST || type == TYPE_APPLICATION_OVERLAY) {
val windowToken = root.decorView.windowToken
val appToken = root.decorView.applicationWindowToken
if (windowToken === appToken) {
// windowToken == appToken means this window isn't contained by any other windows.
// if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
return true
}
}
// Method is called again if false is returned which is useful because a toast may take some time to pop up. But for
// obvious reasons an infinite wait isn't of help. So false is only returned as often as maxFailures specifies.
return (++failures >= maxFailures)
}
companion object {
/** Default for maximum number of retries to wait for the toast to pop up */
private const val DEFAULT_MAX_FAILURES = 5
fun onToast(text: String, maxRetries: Int = DEFAULT_MAX_FAILURES) = onView(withText(text)).inRoot(isToast(maxRetries))!!
fun onToast(textId: Int, maxRetries: Int = DEFAULT_MAX_FAILURES) = onView(withText(textId)).inRoot(isToast(maxRetries))!!
fun isToast(maxRetries: Int = DEFAULT_MAX_FAILURES): Matcher<Root> {
return ToastMatcher(maxRetries)
}
}
}
I hope this will be of help for later readers - the usage is described in the comment.

I write my custom toast matcher:
import android.view.WindowManager
import androidx.test.espresso.Root
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
class ToastMatcher : TypeSafeMatcher<Root>() {
override fun describeTo(description: Description) {
description.appendText("is toast")
}
override fun matchesSafely(root: Root): Boolean {
val type = root.getWindowLayoutParams().get().type
if (type == WindowManager.LayoutParams.TYPE_TOAST) {
val windowToken = root.getDecorView().getWindowToken()
val appToken = root.getDecorView().getApplicationWindowToken()
if (windowToken === appToken) {
return true
}
}
return false
}
}
And use like this:
onView(withText(R.string.please_input_all_fields)).inRoot(ToastMatcher()).check(matches(isDisplayed()))

For kotlin, I had to use the apply extension function, and this worked for me.
1- declare your ToastMatcher class in the androidTest folder:
class ToastMatcher : TypeSafeMatcher<Root?>() {
override fun matchesSafely(item: Root?): Boolean {
val type: Int? = item?.windowLayoutParams?.get()?.type
if (type == WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW) {
val windowToken: IBinder = item.decorView.windowToken
val appToken: IBinder = item.decorView.applicationWindowToken
if (windowToken === appToken) { // means this window isn't contained by any other windows.
return true
}
}
return false
}
override fun describeTo(description: Description?) {
description?.appendText("is toast")
}
}
2- Then you use like this to test that the toast message actually displays
onView(withText(R.string.invalid_phone_number))
.inRoot(ToastMatcher().apply {
matches(isDisplayed())
});
Attribution to ToastMatcher class:
/**
* Author: http://www.qaautomated.com/2016/01/how-to-test-toast-message-using-espresso.html
*/

I would say for toast messages first define your rule
#Rule
public ActivityTestRule<AuthActivity> activityTestRule =
new ActivityTestRule<>(AuthActivity.class);
then whatever toast message text you are looking for type it in between quotation
for example I used "Invalid email address"
onView(withText("Invalid email address"))
.inRoot(withDecorView(not(activityTestRule.getActivity().getWindow().getDecorView())))
.check(matches(isDisplayed()));

I would like to suggest an alternative method, especially if you need to check that particular toast is NOT displayed
The problem here that
onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(matches(not(isDisplayed())))
or
onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(doesNotExist())
or any other custom inRoot checks
are throwing NoMatchingRootException even before the code passes to check method
You may just catch the exception and complete the test but that's not a good option since throwing and catching NoMatchingRootException consumes a lot of time in a comparison with the default test case. Seems that Espresso is waiting for the Root for a while
For this case is suggest just to give up with espresso here and use UiAutomator for this assertion. The Espresso and UiAutomator frameworks could easily work together in one environment.
val device: UiDevice
get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun assertPopupIsNotDisplayed() {
device.waitForIdle()
assertFalse(device.hasObject(By.text(yourText))))
}
fun assertPopupIsDisplayed() {
device.waitForIdle()
assertTrue(device.hasObject(By.text(yourText))))
}

Using ActivityScenarioRule and Java
Some imports for the code
import android.view.View;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
1. Declare the rule
//Change YourActivity by the activity you are testing
#Rule
public ActivityScenarioRule<YourActivity> activityRule
= new ActivityScenarioRule<>(YourActivity.class);
2. Initialize the decor view
private View decorView;
#Before
public void loadDecorView() {
activityRule.getScenario().onActivity(
activity -> decorView = activity.getWindow().getDecorView()
);
}
3. Finally test it
#Test
public void testWithToasts() {
//Arrange and act code
//Modify toast_msg to your own string resource
onView(withText(R.string.toast_msg)).
inRoot(RootMatchers.withDecorView(not(decorView)))
.check(matches(isDisplayed()));
}

I'm pretty new to this, but I made a base class 'BaseTest' that has all of my actions (swiping, clicking, etc.) and verifications (checking text views for content, etc.).
protected fun verifyToastMessageWithText(text: String, activityTestRule: ActivityTestRule<*>) {
onView(withText(text)).inRoot(withDecorView(not(activityTestRule.activity.window.decorView))).check(matches(isDisplayed()))
}
protected fun verifyToastMessageWithStringResource(id: Int, activityTestRule: ActivityTestRule<*>) {
onView(withText(id)).inRoot(withDecorView(not(activityTestRule.activity.window.decorView))).check(matches(isDisplayed()))
}

this works for me
onView(withId(R.id.inputField)).check(matches(withText("Lalala")));

The way Toasts are implemented makes it possible to detect a toast has been displayed. However there is no way to see if a Toast has been requested, thru a call to show()) or to block between the period of time between show() and when the toast has become visible. This is opens up unresolvable timing issues (that you can only address thru sleep & hope).
If you really really want to verify this, here's a not-so-pretty alternative using Mockito and a test spy:
public interface Toaster {
public void showToast(Toast t);
private static class RealToaster {
#Override
public void showToast(Toast t) {
t.show();
}
public static Toaster makeToaster() {
return new RealToaster();
}
}
Then in your test
public void testMyThing() {
Toaster spyToaster = Mockito.spy(Toaster.makeToaster());
getActivity().setToaster(spyToaster);
onView(withId(R.button)).perform(click());
getInstrumentation().runOnMainSync(new Runnable() {
#Override
public void run() {
// must do this on the main thread because the matcher will be interrogating a view...
Mockito.verify(spyToaster).showToast(allOf(withDuration(Toast.LENGTH_SHORT), withView(withText("hello world"));
});
}
// create a matcher that calls getDuration() on the toast object
Matcher<Toast> withDuration(int)
// create a matcher that calls getView() and applies the given view matcher
Matcher<Toast> withView(Matcher<View> viewMatcher)
another answer regarding this
if(someToast == null)
someToast = Toast.makeText(this, "sdfdsf", Toast.LENGTH_LONG);
boolean isShown = someToast.getView().isShown();

Related

Testing an MVP Android Application with Mockito

I'm trying to do some unit test on a basic Android app. it just do a login to some WS using retrofit my app has a MVP pattern.
What I'm doing?
call to presenter layer this will call to interactor and here I will call to my service
#Override
public void doLogin(String user, String pwd, final LoginListener loginListener) {
try {
final LoginRequest request = new LoginRequest();
request.setEmpleado(user);
request.setPwd(pwd);
Callback<LoginResponse> callback = new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if(response != null && response.isSuccessful() && response.body() != null) {
if("00".equals(response.body().getCodigo())) {
loginListener.authOK();
} else {
loginListener.showError();
}
} else {
loginListener.showError();
}
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
"+t.getMessage()+" "+t.getCause());
if(t instanceof SocketTimeoutException) {
loginListener.showError();
} else {
loginListener.showError();
}
}
};
WSLogin wsLogin = RetrofitClient.getInstance().getRetrofit().create(WSLogin.class);
wsLogin.autenticar(request).enqueue(callback);
} catch (Exception e) {
loginListener.showError();
e.printStackTrace();
}
My service it's called but i never get into callback
Test
package com.arleckk.loginmvpci.login;
import com.arleckk.loginmvpci.login.presenter.LoginListener;
import com.arleckk.loginmvpci.login.presenter.LoginPresenter;
import com.arleckk.loginmvpci.login.presenter.LoginPresenterImpl;
import com.arleckk.loginmvpci.login.view.LoginView;
import com.arleckk.loginmvpci.model.LoginResponse;
import com.arleckk.loginmvpci.network.WSLogin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Response;
import static org.junit.Assert.assertEquals;
#RunWith(PowerMockRunner.class)
#PowerMockIgnore("javax.net.ssl.*")
public class LoginTest {
#Mock private LoginView loginView;
#Mock private LoginPresenter loginPresenter;
#Mock private LoginListener loginListener;
#Mock private Call<LoginResponse> loginResponseCall;
Response<LoginResponse> loginResponseResponse;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
loginPresenter = new LoginPresenterImpl(loginView);
}
#Test
public void testOK() throws IOException {
loginPresenter.doLogin("TEST", "TEST1234");
}
}
Another question is: do am I really doing unit test? I mean unit test only test a "Unit" of the code.
I'm expecting a LoginResponse object and then compare it and if it is equals to "00" it's sucessful
No, you're not. For a few reasons. First off, you don't have a test here. You don't have a single assertion. No matter what happens your code will say pass. So it isn't a test. A test would be to say call doLogin with a working login, and ensure that loginListener.authOK() is called. Then another test that uses a bad password, and checks that showError is called. Without that all you have is a waste of time.
Second- this is horribly scoped for a unit test. For a unit test, you should be checking that the smallest unit of code works. Here, you're checking that your entire networking stack, and your server, all work. That's way too much. And a unit test should never depend on an outside server working, that will just lead to a flaky test- something like that should be in an integration suite.
Your code right now isn't optimized for testing. If it was- you wouldn't be creating the retrofit client via a singleton in the code. You'd pass it into doLogin. That way you could pass in a mock in a test that could return a fake response, and then test whether your code can correctly identify the a fake success and failure response correctly, removing the server dependency yet testing all of the functionality.

How to implement Java step Builder pattern in Kotlin

In my current Android project I am investigating the use of Kotlin.
I am rewriting a 100% Java Android app to 100% Kotlin.
I'm stuck with attempting to implement my Java step builders though.
I employed Java step Builders as they force users of my code to supply all required data and/or functions before being able to execute an associated RxJava process.
These RxJava processes are complex and I wished to simplify their initialisation and execution as much as possible.
The use of Java Step builders allow developers to write the following code:-
Sequence.builder()
.stepOne(one)
.stepTwo(two)
.stepThree(three)
.build()
.execute();
What I am looking for is a Kotlin version of this approach.
My initial thought was that Kotlin would support Builders and Step Builders.
I am not "precious" about employing Builders in Kotlin, the Kotlin solution must force the developers that use my code to have supplied all required data and/or functions before they are able to execute the associated "executed" code.
From investigating Kotlin I've discovered internal DSLs which are sounding both interesting topic in themselves and a possible solution to this particular question.
I have a number of Step Builders to implement, none of these have more than 6 parameters. I do like to try and keep to SOLID no more than three parameters rule though.
Also if it makes any difference, some of the passed parameters are RxJava Actions and Consumers. Default values are not relevant here as none of the parameters have viable default values.
UPDATE
My Java step builders all resemble this:-
public class ExampleSequence extends Sequence {
private static final String TAG = "ExampleSequence";
private final Action onComplete;
private final Consumer<? super Throwable> onError;
/**
* #param builder
*/
private ExampleSequence(final Builder builder) {
super(builder.getDoLoginRefreshFail());
this.onError = builder.getOnError();
this.onComplete = builder.getOnComplete();
}
/**
*
*/
public static OnCompleteAction builder() {
return new Builder();
}
public interface OnCompleteAction {
onErrorAction onComplete(#NonNull final Action onComplete);
}
public interface onErrorAction {
DoLoginRefreshFail onError(#NonNull final Consumer<? super Throwable> onError);
}
public interface DoLoginRefreshFail {
Build doLoginRefreshFail(#NonNull final Action doLoginRefreshFail);
}
public interface Build {
ExampleSequence build();
}
#SuppressLint("CheckResult")
public void execute() {
final AtomicInteger retryCounter = new AtomicInteger(0);
final Observable<Response<GraphqlQueryResponse>> feedArticles = getPageAndNextInboxArticles(offset, limit)
.onErrorResumeNext(manufactureResumeNext())
.subscribeOn(Schedulers.io());
final Observable<Response<GraphqlQueryResponse>> readingListArticles = getPageAndReadingListArticles(readingListoffset, limit)
.onErrorResumeNext(manufactureResumeNext())
.subscribeOn(Schedulers.io());
login()
.flatMap(...)
.ignoreElement()
.andThen(...)
.andThen(...)
.ignoreElements()
.andThen(...)
.flattenAsObservable(x -> x)
.flatMapCompletable(...)
.retryWhen(errors -> errors.flatMap(e -> constructRetryHandler(retryCounter, e)))
.doOnComplete(onComplete)
.doOnError(onError)
.doAfterTerminate(doAfterTerminate())
.doOnSubscribe(compositeDisposable::add)
.blockingAwait();
}
/**********************************************************************************
*
* BUILDER
*
*/
public static class Builder implements OnCompleteAction, onErrorAction, DoLoginRefreshFail, Build {
private Action onComplete;
private Consumer<? super Throwable> onError;
private Action doLoginRefreshFail;
/***********************************************************************
*
*/
#Override
public ExampleSequence build() {
return new ExampleSequence(this);
}
#Override
public onErrorAction onComplete(#NonNull final Action onComplete) {
this.onComplete = onComplete;
return this;
}
#Override
public DoLoginRefreshFail onError(#NonNull final Consumer<? super Throwable> onError) {
this.onError = onError;
return this;
}
#Override
public Build doLoginRefreshFail(#NonNull final Action doLoginRefreshFail) {
this.doLoginRefreshFail = doLoginRefreshFail;
return this;
}
/**
* #return the onError
*/
Consumer<? super Throwable> getOnError() {
return onError;
}
/**
* #return the onComplete
*/
Action getOnComplete() {
return onComplete;
}
Action getDoLoginRefreshFail() {
return doLoginRefreshFail;
}
}
}
The step builder pattern in Kotlin is completely doable, and I've provided an example of it that mirrors the Java example you provided.
class ExampleSequence private constructor(builder: Builder): Sequence(builder.doLoginRefreshFail) { //This is your "super()" call.
//This is equivalent to assigning the final variables [onComplete] and [onError] in the class constructor
private val onComplete = builder.onComplete
private val onError = builder.onError
//More info about companion objects here: https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects
companion object {
//Java will see this as [ExampleSequence.Companion.builder()] unless you add this annotation
#JvmStatic
fun builder(): OnCompleteAction = Builder()
}
fun execute() {
//Do your stuff here...
}
//The following classes and interfaces are similar to being static inner classes. If you want the classes to access
//fields of the enclosing outer class, you must use the keyword [inner] before declaring the class. Example:
// inner class Foo { ... }
interface OnCompleteAction {
fun onComplete(onComplete: Action): onErrorAction
}
interface DoLoginRefreshFail {
fun doLoginRefreshFail(doLoginRefreshFail: Action): Build
}
interface onErrorAction {
fun onError(onError: Consumer<in Throwable>): DoLoginRefreshFail //The [in] keyword is the same as saying Consumer<? super Throwable>
}
interface Build {
fun build(): ExampleSequence
}
class Builder: OnCompleteAction, onErrorAction, DoLoginRefreshFail, Build {
//The [lateinit] keyword states that this variable will be initialized later. Calling it before it is initialized will throw an exception
lateinit var onComplete: Action
private set //Only this class can modify.
lateinit var onError: Consumer<in Throwable>
private set
lateinit var doLoginRefreshFail: Action
private set
//No special differences here... oooh, inlined [override] keyword!
override fun onComplete(onComplete: Action): onErrorAction {
this.onComplete = onComplete
return this
}
override fun doLoginRefreshFail(doLoginRefreshFail: Action): Build {
this.doLoginRefreshFail = doLoginRefreshFail
return this
}
override fun onError(onError: Consumer<in Throwable>): DoLoginRefreshFail {
this.onError = onError
return this
}
override fun build(): ExampleSequence = ExampleSequence(this)
//Where are the getter methods? If you look at the variable declarations, they are public by default.
//This means that these variables are public read, but can only be set by this class only. In other words, built-in getter!
}
}
However, in a pure Kotlin project, step builder is sort of an anti-pattern. With default and named parameters built into the language, you can actually achieve SOLID by a simple data class. Taking the ExampleSequence class for example, your solution could look something like:
data class ExampleSequence(
private val onComplete: Action,
private val onError: Consumer<in Throwable>,
private val doLoginRefreshFail: Action,
private val aNewParam: String = "Default")
: Sequence(doLoginRefreshFail) { //This is your "super()" call.
fun execute() {
//Do your stuff here...
}
}
fun foo() {
//Example of using named parameters and passing in variables. Notice parameters aren't in the same order as how it is declared in the class
ExampleSequence(
onError = Consumer(),
onComplete = Action(),
doLoginRefreshFail = Action()
).execute()
//Since I added [aNewParam], instead of using the default, let's change it.
ExampleSequence(
onError = Consumer(),
onComplete = Action(),
doLoginRefreshFail = Action(),
aNewParam = "Something else!"
).execute()
}
Here is a nice article going into a bit more detail: https://dev.to/chrisvasqm/avoiding-the-builder-design-pattern-in-kotlin-3b1a
Also, in case you need another example of a step builder pattern in Kotlin, you might want to check this out too: https://www.baeldung.com/kotlin-builder-pattern

How to test Evernote's Android Jobs?

How do I test Jobs created with Android-Job library? Any ideas on unit testing, instrumented testing or even manual testing are appreciated, I just want to check if it works as expected.
To be specific, I have a job that performs an HTTP request every N hours:
package com.kondenko.yamblzweather.job;
import android.support.annotation.NonNull;
import com.evernote.android.job.Job;
import com.evernote.android.job.JobRequest;
import com.kondenko.yamblzweather.model.entity.WeatherData;
import com.kondenko.yamblzweather.ui.weather.WeatherInteractor;
import com.kondenko.yamblzweather.utils.SettingsManager;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
public class UpdateWeatherJob extends Job {
private WeatherInteractor interactor;
private String cityId;
private String units;
private long refreshRateHr;
// Do not delete, needed for job creation
public static final String TAG = "UpdateWeaterJob";
#Inject
public UpdateWeatherJob(WeatherInteractor interactor, SettingsManager settingsManager) {
this.interactor = interactor;
this.cityId = settingsManager.getSelectedCity();
this.units = settingsManager.getSelectedUnitValue();
this.refreshRateHr = settingsManager.getRefreshRate();
}
#NonNull
#Override
protected Result onRunJob(Params params) {
WeatherData data = interactor.getWeather(cityId, units).blockingGet();
return data != null ? Result.SUCCESS : Result.FAILURE;
}
public void buildJobRequest(String name) {
new JobRequest.Builder(UpdateWeatherJob.TAG)
.setPeriodic(TimeUnit.HOURS.toMillis(refreshRateHr))
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setPersisted(true)
.build()
.schedule();
}
}
We at Evernote test jobs the following way:
Unit tests -> We tend to extract the logic into actions, similar to presenters in a MVP setup. This removes Android dependencies and the actions are unit testable.
QA -> We have QA options to trigger jobs manually. This way our QA team can verify that the job produces the correct output
Verifying timing -> There we rely on the logs.
You also should take a look at these slides. ADB can be really helpful to verify curtain assumptions.

Need help to write a unit test using Mockito and JUnit4

Need help to write a unit test for the below code using Mockito and JUnit4,
public class MyFragmentPresenterImpl {
public Boolean isValid(String value) {
return !(TextUtils.isEmpty(value));
}
}
I tried below method:
MyFragmentPresenter mMyFragmentPresenter
#Before
public void setup(){
mMyFragmentPresenter=new MyFragmentPresenterImpl();
}
#Test
public void testEmptyValue() throws Exception {
String value=null;
assertFalse(mMyFragmentPresenter.isValid(value));
}
but it returns following exception,
java.lang.RuntimeException: Method isEmpty in android.text.TextUtils
not mocked. See http://g.co/androidstudio/not-mocked for details. at
android.text.TextUtils.isEmpty(TextUtils.java) at ....
Because of JUnit TestCase class cannot use Android related APIs, we have to Mock it.
Use PowerMockito to Mock the static class.
Add two lines above your test case class,
#RunWith(PowerMockRunner.class)
#PrepareForTest(TextUtils.class)
public class YourTest
{
}
And the setup code
#Before
public void setup() {
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return !(a != null && a.length() > 0);
}
});
}
That implement TextUtils.isEmpty() with our own logic.
Also, add dependencies in app.gradle files.
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
Thanks Behelit's and Exception's answer.
Use PowerMockito
Add this above your class name, and include any other CUT class names (classes under test)
#RunWith(PowerMockRunner.class)
#PrepareForTest({TextUtils.class})
public class ContactUtilsTest
{
Add this to your #Before
#Before
public void setup(){
PowerMockito.mockStatic(TextUtils.class);
mMyFragmentPresenter=new MyFragmentPresenterImpl();
}
This will make PowerMockito return default values for methods within TextUtils
You would also have to add the relevant gradle depedencies
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
This is a known issue as mentioned by #Exception. In my case, I also stumbled upon same situation but on advice of senior dev decided to use Strings.isNullOrEmpty() instead of TextUtils.isEmpty(). It turned out to be a nice way to avoid it.
Update: I should better mention it that this utility function Strings.isNullOrEmpty() requires Guava library.
This is a known issue, due to a clause in Testing Fundamental of Android which says:
You can use the JUnit TestCase class to do unit testing on a class
that does not call Android APIs.
The default behavior is problematic when using classes like Log or TextUtils.
To sum up:
android.jar is mock before, so some Android API return value may not be as expected.
JUnit itself is a single measure for the java code, so try not to use the Android API methods.
Source: http://www.liangfeizc.com/2016/01/28/unit-test-on-android/
add this line in your gradle file in case of Android Studio.
android{
....
testOptions {
unitTests.returnDefaultValues = true
}
}
You should use Robolectric:
testImplementation "org.robolectric:robolectric:3.4.2"
And then
#RunWith(RobolectricTestRunner::class)
class TestClass {
...
}
Solution 1:
I would like to provide a Kotlin and a Java version.
Kotlin version:
import android.text.TextUtils
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Matchers.any
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
#RunWith(PowerMockRunner::class)
#PrepareForTest(TextUtils::class)
class UserOwnedDataTest1 {
#Before
fun setup() {
PowerMockito.mockStatic(TextUtils::class.java)
PowerMockito.`when`(TextUtils.isEmpty(any(CharSequence::class.java))).thenAnswer { invocation ->
val a = invocation.arguments[0] as? CharSequence
a?.isEmpty() ?: true
}
}
}
Java version:
import android.text.TextUtils;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.any;
#RunWith(PowerMockRunner.class)
#PrepareForTest(TextUtils.class)
public final class UserOwnedDataTest2 {
#Before
public void setup() {
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer((Answer<Boolean>) invocation -> {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return !(a != null && a.length() > 0);
});
}
}
Do not forget to add the dependencies:
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
I remember that we still need another dependency, but not clearly.
Anyway you could fix the missing dependency easily.
Solution 2:
Or you could add the same package and class name with TextUtils
package android.text;
public class TextUtils {
public static boolean isEmpty( CharSequence str) {
return str == null || str.length() == 0;
}
}
I was able to solve this error by running the test class with the following.
#RunWith(RobolectricGradleTestRunner.class)
public class MySimpleTest {
.....a bunch of test cases
}
This wiki page explains in greater detail
https://github.com/yahoo/squidb/wiki/Unit-testing-with-model-objects
I replaces everywhere in my project TextUtils.isEmpty(...) with this:
/**
* Util class to be used instead of Android classes for Junit tests.
*/
public class Utils {
/**
* Returns true if the string is null or 0-length.
* #param str the string to be examined
* #return true if str is null or zero length
*/
public static boolean isEmpty(#Nullable CharSequence str) {
return str == null || str.length() == 0;
}
}
As a followup to Johnny's answer, to catch TextUtils.isEmpty(null) calls as well, you could use this piece of code.
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any()))
.thenAnswer((Answer<Boolean>) invocation -> {
Object s = invocation.getArguments()[0];
return s == null || s.length() == 0;
});
With newer power mock (2.0.9) and mockito (3.9.0) I had. to change execution to this one:
when(TextUtils.isEmpty(any())).thenAnswer((Answer<Boolean>) invocation -> {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return a == null || a.length() == 0;
});

Unit test static method with dependency

I have not much experience in unit testing, especially with Mockito and now I have encountered the following situation.
class A {
void setField(String obj) {
}
Object execute() {
throw new RuntimeException("Meh!");
}
}
class B {
//function to be tested
static Object someMethod() {
A a = new A();
a.setField("test");
Object response = a.execute();
//logic here
return response;
}
}
class BTest() {
A aInstance = mock(A.class);
#Test
public void test_someMethod_when_exec_returns_X() {
when(aInstance.execute()).thenReturns("X");// doesn’t work
assertTrue("X", B.someMethod());
}
}
I want to test the someMethod static method when a.execute() returns specific value.
I know, I can create a mock object of A and pass it to someMethod function, which is not a good solution as I should change the signature of someMethod.
What is the correct solution in this case?
If you check out PowerMockito's documentation you'll realize that the following is what you need:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.whenNew;
// execute the test with the appropriate runner
#RunWith(PowerMockRunner.class)
// prepare B for instrumentation so we can hack stuff inside
#PrepareForTest(B.class)
public class MyTest {
#Test
public void bShouldCallA() throws Exception {
// create a mock for A and configure its behaviour
A aMock = mock(A.class);
when(aMock.execute()).thenReturn("X");
// make sure that when A's constructor is called in the static method, the mock above is returned
whenNew(A.class).withNoArguments().thenReturn(aMock);
// do the actual invocation
Object actualResult = B.someMethod();
// check result and interactions
assertEquals("X", actualResult);
verify(aMock).setField("test");
}
}
As I mentioned, the PowerMockito doesn't work in android, you can just mock android object with that. And here comes engineering solution :)
Factory class to create object A.
public class AFactory {
static private AFactory sInsntance = new AFactory();
public static AFactory createObject() {
return sInsntance.createInternally();
}
protected TMMethodBuilder createInternally() {
return new A();
}
//This function is only for testing, in order to inject factory
#Deprecated
public static void setFactory(AFactory mock) {
sInsntance = mock;
}
}
And create object A:
A a = AFactory.createObject();
In Test project extend AFactory and override createInternally() method to return mocked object.
public class AFactoryTest extends AFactory {
private static A a = mock(A.class);
#Override
protected TMMethodBuilder createInternally() {
return a;
}
}
So in test class just do the following:
factory = new AFactoryTest();
a = factory.createInternally();
AFactory.setFactory(factory);
//
when(..).thenReturn();

Categories

Resources