azimolabs / conditionwatcher Goto Github PK
View Code? Open in Web Editor NEWAndroid tool which helps to synchronise application behaviours with test thread in automation tests.
Android tool which helps to synchronise application behaviours with test thread in automation tests.
The better approach will be to convert in kotlin and use delay so it does not block the main thread and also we could do parallel testing as well
First, thanks for this tool! IdlingResource is a total mess.
Now to business, from the documentation I was not able to understand how to get the latest activity.
However, I found a different way, which follows:
//ref: http://stackoverflow.com/a/38990078/689223
static private Activity getCurrentActivity(){
Collection<Activity> resumedActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
for(Activity act : resumedActivity){
return act;
}
return null;
}
static private Activity getCurrentActivitySafe(){
Callable<Activity> callable = new Callable<Activity>() {
@Override
public Activity call() throws Exception {
return getCurrentActivity();
}
};
FutureTask<Activity> task = new FutureTask<>(callable);
getInstrumentation().runOnMainSync(task);
try {
return task.get(); // Blocks
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
Example on how to use:
@Override
public boolean checkCondition() {
Activity currentActivity = getCurrentActivitySafe();
(...)
}
getCurrentActivitySafe()
has to be called instead getCurrentActivity()
because checkCondition
can run outside of the UI thread, causing an error.
My suggestion is to make getCurrentActivitySafe()
an utility function, or to rename it to getCurrentActivity()
and make it a method of Instruction
.
Another alternative is to make checkCondition
a method that receives the current activity as an argument, like this checkCondition(Activity currentActivity)
Thank you for this nice approach to ui testing.
Could you please make minsdk smaller? 16 is too strict.
Adnroid Studio 3.3.
In my app I use Retrofit to do http request. Also I use MockWebServer to return stub response.
On activity when I click button I start async (call.enqueue(callback);
) http request (by Retrofit 2) and wait callback method to return response. Here Espresso's test:
@RunWith(AndroidJUnit4::class)
class AddTraderActivityTest {
@get:Rule
var addTraderIntentTestRule: IntentsTestRule<AddTraderActivity> = IntentsTestRule(AddTraderActivity::class.java)
private val baseEditText = viewWithId(baseTextInputEditText)
private val quoteEditText = viewWithId(quoteTextInputEditText)
private val buttonStart = viewWithId(startButton)
@Before
fun setup() {
mockServer = MockWebServer()
mockServer.start(8081)
Debug.d(TAG, "SUCCCESS_START_MOCKWEBSRVER")
}
@Test
fun buttonStart_click_longResponse() {
// stub response
mockServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(FileUtil.getStringFromFile(context, "add_trader_success_200.json"))
.setBodyDelay(5000, TimeUnit.MILLISECONDS))
onView(withId(R.id.baseTextInputEditText))
.perform(typeText(BASE_TEST))
onView(withId(R.id.quoteTextInputEditText))
.perform(typeText(QUOTE_TEST))
onView(withId(R.id.startButton))
.perform(click())
onView(withText(R.id.containerProgressBarLayout))
.check(matches(isDisplayed()))
}
But problem is when execute perform(click())
the method check is not call until not get stub response (after 5 seconds).
But I need call method check
immediately after perform(click())
method. Because I need to check is containerProgressBarLayout
is isDisplayed()
while not return stub response. I need to check my view DURING loading data.
So to do this I try this:
@Test
fun buttonStart_click_longResponse() {
// stub response
mockServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(FileUtil.getStringFromFile(context, "add_trader_success_200.json"))
.setBodyDelay(5000, TimeUnit.MILLISECONDS))
baseEditText.type(BASE_TEST)
quoteEditText.type(QUOTE_TEST)
buttonStart.click()
Debug.d(TAG, "AFTER_CLICK")
ConditionWatcher.waitForCondition(object : Instruction() {
override fun getDescription(): String {
return "test description"
}
override fun checkCondition(): Boolean {
Debug.d(TAG, "checkCondition")
return true
}
})
onView(withText(containerProgressBarLayout))
.check(matches(isDisplayed()))
}
But log AFTER_CLICK
and method checkCondition()
call AFTER 5 SECONDS.
When call click
then call this production code:
TransportService.executeTraderOperation(Trader.Operation.CREATE, base.trim(), quote.trim(), new DefaultRestClientCallback<Void>() {
@Override
public void onSuccess(Response<Void> response) {
}
@Override
public void onError(ErrorResponse errorResponse) {
}
});
}
and this:
public static void executeTraderOperation(Trader.Operation traderOperation, String base, String quote, Callback<Void> callback) {
TraderMonitorRestClient traderMonitorRestClient = RestClientFactory.createRestClient(TraderMonitorRestClient.class);
String sender = BuildConfig.APPLICATION_ID + "_" + BuildConfig.VERSION_NAME;
String key = DateUtil.getDateAsString(new Date(), "mmHHddMMyyyy");
Call<Void> call = traderMonitorRestClient.executeTraderOperation(traderOperation.toString().toLowerCase(), base, quote, sender, key);
// asynchronously
call.enqueue(callback);
}
fun ViewInteraction.click(): ViewInteraction = perform(ViewActions.click())
fun viewWithId(@IdRes resId: Int): ViewInteraction {
return onView(withId(resId))
}
Thanks for ConditionWatcher
! It really helped get me away from littering production code with references to Idling Resources.
In our project (which uses Kotlin), we've added some helper functions to allow us to tie directly into Espresso's functions so that we can mimic the withId()
and .check(matches(...))
syntax. This allows us to do this:
waitForView(withId(R.id.searchfield_clearbutton)).toMatch(allOf(isDisplayed(), isEnabled()))
This eliminates the need for having to deal with activity or test context, and the implementation is fairly trivial (again, this is Kotlin):
/**
* Waits for the view specified by the matcher meats the specified condition
*/
fun waitForView(viewMatcher: Matcher<View>) = ViewMatcherWaiter(viewMatcher)
/**
* Used by the waitForView() shorthand fluent function
*/
class ViewMatcherWaiter constructor(val viewMatcher: Matcher<View>) {
/**
* Specify the Espresso matches which will satisfy the condition
*/
fun toMatch(viewChecker: Matcher<View>) = waitForCondition(object : Instruction() {
override fun getDescription(): String {
val desc = StringDescription()
desc.appendText("Wait for view ")
viewMatcher.describeTo(desc)
desc.appendText(" to match ")
viewChecker.describeTo(desc)
return desc.toString()
}
override fun checkCondition(): Boolean {
return try {
onView(viewMatcher).check(matches(viewChecker))
true
} catch (x: AssertionError) {
false
}
}
})
}
It also produces nice error messages on timeout:
java.lang.Exception: Wait for view with id: your.package.here.mock:id/searchfield_clearbutton to match (is displayed on the screen to the user and is enabled) - took more than 5 seconds. Test stopped.
This feels very natural to people familiar with Espresso testing and I think it would make a nice addition to your library. Your matchers can get as complex as Espresso allows without having to write custom Instruction
classes.
I think it makes more sense to make the interval/timeout be instruction specific then be on on the main ConditionWatcher class.
We can keep defaults but I feel like its safer for the defaults to reside in Instruction
I'm also willing to make the PR for it.
Thoughts?
Guys, I've added your tool to my test, I need my test waits and checks Visibility of 2 buttons then finishes the test.
In my test:
ConditionWatcher.waitForCondition(new BtnSendVerificationInstruction());
onView(withId(R.id.btnSendVerification)).perform(click());`
The Instruction:
public class BtnSendVerificationInstruction extends Instruction {
@Override
public String getDescription() {
return "BtnSendVerification should be moved to center of activity";
}
@Override
public boolean checkCondition() {
Activity activity = ((TestApplication)
InstrumentationRegistry.getTargetContext().getApplicationContext()).getCurrentActivity();
if (activity == null) return false;
Button btnSendVerification = (Button) activity.findViewById(R.id.btnSendVerification);
boolean isDisplayed = btnSendVerification.isShown();
return btnSendVerification != null && isDisplayed;
}
}
Any idea?
Thanks
https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
Please migrate to Maven Central.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.