So i have a an android application project and unit test project to it. I am a beginner in Eclipse and maybe i don't understand properly how to work with JUnit.
I have written two simple test methods:
#Test
public void searchTest() {
fail("test is failed");
}
#Test
public void getTest() {
assertTrue(10 > 1);
}
So my problems are
Firstly: It is obvoius that searchTest must fail but when i run it doesn't happend :
Secondly: in some reason i can not debug my test methods. I click right button on serachTest in JUnit dialogue and select "Debug" but debugging is not starting.
Thirdly: Only one test method is showing in in JUnit dialogue however i have two methods with #Test annotation in my class. Why does it happend?
Could anyone help me? Thanks in advance
Think about what you had selected when you said to Run it as a Test. You need to select the class itself rather than a method within it. The JUnit View will only show you the tests that were run.
And you need to set a Breakpoint in your test code to have any observable difference from just Running the test.
Related
I have the following Espresso test. It always passes if I run it by itself, but always fails when I run all the tests in the class together.
What's also a bit strange is that it used to work even as part of the suite. I'm not sure why now it stopped working. It must be something I've done but I don't know what.
#Test
public void itemHasImage_ShowsImage() {
closeSoftKeyboard();
if (mItem.getImageUrl() != null) {
onView(withId(R.id.edit_item_image)).perform(scrollTo())
.check(matches(isDisplayed()));
}
}
The error I'm getting is:
Error performing 'scroll to'...
...
Caused by: java.lang.RuntimeException: Action will not be performed
because the target view does not match one or more of the following
constraints:(view has effective visibility=VISIBLE and is descendant
of a: (is assignable from class: class android.widget.ScrollView...
The view is visible and a descendant of a scroll view, as evidenced by it passing when run on it's own.
When it gets to this test (in the suite) it just doesn't scroll. But when I run it by itself it scrolls just fine.
In another stack overflow question I asked recently Espresso not starting Activity the same for second iteration in parameterised test, I found out that onDestroy from the previous test was getting called after onResume in the current test, causing it to set a value to null and fail the test. Again in that situation, the problem was that the test passed by itself but not in the suite. I now have a similar problem but no obvious way to fix it. (Edit: the workaround for the other question can no longer be applied).
Any ideas anyone? Could it be reading the wrong Activity somehow? Like maybe it's looking at the one from the previous test. That sounds ridiculous but after that last problem I had it seems possible.
Edit: Ok it turns out that when running this test as part of the suite, the image is in fact not visible causing the test to fail. I found this using the debugger and scrolling the view manually. But why?
I think it's a bug and have logged an issue here:
https://code.google.com/p/android/issues/detail?id=235247
It can able to solve using Orchestrator android testing utility library, It will executing each test case independently, so there is no chance of breaking in test suite
When using AndroidJUnitRunner version 1.0 or higher, you have access to a tool called Android Test Orchestrator, which allows you to run each of your app's tests within its own invocation of Instrumentation.
Enabling using build.gradle
android {
defaultConfig {
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
testOptions {
execution 'ANDROID_TEST_ORCHESTRATOR'
}
}
dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestUtil 'com.android.support.test:orchestrator:1.0.1'
}
For more info:https://developer.android.com/training/testing/junit-runner.html#using-android-test-orchestrator
I had a similar issue but the cause was different, #Rakshith-Kumar suggestion of using Orchestrator might work, also putting each test method in a TestSuite could also be a solution, since the flakiness could be addressed by running each test individually.
Also want to mention that if you're testing component that's using RxJava schedulers (io scheduler for example) to run some tasks in a background thread, the flakiness could happen because Espresso will not be able to synchronize with the background threads.
A solution can be injecting a the ObservableTransformer to do the subscribeOn and observeOn operations. And for Espresso tests substitute the ObservableTransformer instance with another one that uses Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR) for the io scheduler.
Something like that:
public <T> ObservableTransformer<T, T> ioTransformer() {
return observable -> observable.subscribeOn(AsyncTask.THREAD_POOL_EXECUTOR).observeOn(AndroidSchedulers.mainThread());
}
This way Espresso can synchronize with background threads and avoid flakiness.
More info:
https://blog.danlew.net/2015/03/02/dont-break-the-chain/
https://developer.android.com/training/testing/espresso/#sync
I've been trying to cover my Android app with tests and have started using espresso recently. Pretty impressed with it so far. However most of my app's functionality requires that users are logged in. And since all tests are independent, this requires registering a new user for each test. This works fine however the time required for each test increases considerably because of this.
I am trying to find a way to register a user once in a class (of tests) and then use that same user account to perform all the tests in that class.
One way I have been able to do this is to actually have only one test (#Test) method that runs all the other tests in the order I want. However this is an all or nothing approach, since the gradle cAT task only outputs the results once at the end without providing info about the intermediate tests that may have passed/failed.
I also tried the #BeforeClass approach which however did not work (no gradle output from the class where I had used this even with the debug option and it seemed like it took a long time before it moved on to the next class of tests).
Is there a better approach to register a user once at start of a class and then logout once at the end of testing?
Any help appreciated.
Ideally you would test the login/logout functionality in a set of tests that just test different login/logout scenarios, and let the other tests focus on other use cases. However, since the other scenarios depend on the user being logged in, it sounds like one way to solve this would be to provide a mock version of the app component handling the login. For the other login dependent tests, you would inject this mock at the start and it would return mock user credentials that the rest of the app can work with.
Here's an example where Dagger, Mockito and Espresso is being used to accomplish this: https://engineering.circle.com/instrumentation-testing-with-dagger-mockito-and-espresso-f07b5f62a85b
I test an app that requires this same scenario. The easiest way I've gotten around this is to split up logging in and out into their own test classes. Then you add all your test classes to a suite, starting and ending with the login and logout suites respectively. Your test suites ends up looking kind of like this.
#RunWith(Suite.class)
#Suite.SuiteClasses({
LoginSetup.class,
SmokeTests.class,
LogoutTearDown.class
})
EDIT: Here is an example of both the LoginSetup and LogoutTearDown tests. This solution really should only be for end-to-end tests and comprise a small portion of your testing efforts. fejd provides a solution for a full testing stack which also needs to be considered.
#LargeTest
public class SmokeSetup extends LogInTestFixture {
#Rule
public ActivityTestRule<LoginActivity> mLoginActivity = new ActivityTestRule<>(LoginActivity.class);
#Test
public void testSetup() throws IOException {
onView(withId(R.id.username_field)).perform(replaceText("username"));
onView(withId(R.id.password_field)).perform(replaceText("password"));
onView(withId(R.id.login_button)).perform(click());
}
}
#LargeTest
public class LogoutTearDown extends LogInTestFixture {
#Rule
public ActivityTestRule<MainActivity> mMainActivity = new ActivityTestRule<>(MainActivity.class);
#Test
public void testLogout() throws IOException {
onView(withId(R.id.toolbar_menu)).perform(click());
onView(withId(R.id.logout_button)).perform(click());
}
}
The approach with logging in with #Before is nice but if your login is slow, your combined test time will be very slow.
Here's a great hack that works. The strategy is simple: run every test in order and fail every test before they get a chance to run if a certain test fails (in this case login test).
#RunWith(AndroidJUnit4.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
#LargeTest
public class YourTestsThatDependsOnLogin {
private static failEverything;
#Before
public void beforeTest() {
// Fail every test before it has a chance to run if login failed
if (failEverything) {
Assert.fail("Login failed so every test should fail");
}
}
#Test
public void test0_REQUIREDTEST_login() {
failEverything = true;
// Your code for login
// Your login method must fail the test if it fails.
login();
failEverything = false; // We are safe to continue.
}
// test1 test2 test3 etc...
}
Pros:
What you asked for works and it is fast (if your login is slow)
You can have multiple tests that depend on different logins, meaning you can do a bunch of tests for user1, then a bunch of tests for user2 etc.
Quick to set up.
Cons:
Not standard procedure and someone might wonder why so many tests
fail...
You should mock users instead of actually logging in. You should test your login separately and tests should not depend on each other.
Add the following function in your test file, replace the code in the try
block with yours that performs the login actions.
#Before
fun setUp() {
// Login if it is on the LoginActivity
try {
// Type email and password
Espresso.onView(ViewMatchers.withId(R.id.et_email))
.perform(ViewActions.typeText("a_test_account_username"), ViewActions.closeSoftKeyboard())
Espresso.onView(ViewMatchers.withId(R.id.et_password))
.perform(ViewActions.typeText("a_test_account_password"), ViewActions.closeSoftKeyboard())
// Click login button
Espresso.onView(ViewMatchers.withId(R.id.btn_login)).perform(ViewActions.click())
} catch (e: NoMatchingViewException) {
//view not displayed logic
}
}
With this #Before annotation, this setUp function will be executed before any other tests you have in this test file. If the app lands on Login Activity, do the login in this setUp function. The example here assumes there is an EditText for email and password, as well as a login button. It uses Expresso to type the email and password, then hit the login button. The try catch block is to ensure if you are not landing on the Login Activity, it will catch the error and do nothing, and if you didn't land on the Login Activity, then you are good to go on other tests anyway.
Note: this is Kotlin code, but it looks very similar to Java.
My Application also requires the user to be logged in through-out the test run.
However, I am able to login the first time and the application remembers my username/password throughout the test run. In fact, it remembers the credentials until I force it to forget them or uninstall and install the app again.
During a test run, after every test, my app goes to the background and is resumed again at the beginning of the next test. I am guessing your application requires a user to enter their credentials every time you bring it to the front from the background (banking application maybe?). Is there a setting in your application that will "Remember your credentials"? If yes, you can easily enable it right after you login for the first time in your test run.
Other than that, I think you should talk to the developers about providing you a way to remember your credentials.
If you are using #BeforeClass in Kotlin, you need to place it inside companion object. That way the block under #BeforeClass will be executed just once before the 1st test runs in the class. Also have #AfterClass inside companion object, so that it runs at the very end of the last test of the class.
Keep in mind that once the compiler moves from inside companion object to outside of it, the context of the app under test is lost. You can get back the context by launching the main activity(activity after login) of your app.
companion object {
#ClassRule
#JvmField
val activity = ActivityTestRule(Activity::class.java)
#BeforeClass
#JvmStatic
fun setUp() {
// login block
}
#AfterClass
#JvmStatic
fun tearDown() {
// logout block
}
}
#Test
fun sampleTest() {
activity.launchActivity(Intent())
}
I am having trouble making the instrumentation test using the Espresso.
I have an activity where account picker is popup-ed when app is started (main activity).
If customer clicks on cancel (in dialog), picker is popup up again; If user clicks on add, the result is picked up on activity result.
I dont know how to create a simple test with espresso which will include that picker.
When I create the Instrumentation test with the MainActivity, I got this message:
No activities in stage RESUMED...
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity>{
MainActivity myActivity;
public MainActivityTest(){
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
getActivity();
}
public void testAccountPicker(){
onView(withText("Choose an account")).check(matches(isDisplayed()));
}
}
Does anybody had similar problem?
Thanx for your answers in advance.
That's a tough one :). The problem here is that once the flow leaves your application (Google Account Picker is an external application), Espresso ends the test. Account Picker is an activity from the package com.google.android.gms, thus external. Once it's started, your test is finished and you'll never be able to match anything in the dialog.
You have three possible solutions to make your tests feasible:
Using classpath substitution on your app to fake the intents; or
Fixing your app "testability"; or
Using dependency injection, like Dagger
I'll show how to use classpath substitution. The technique is really simple: you should isolate your Intent creation in a separate class, say IntentsFactory and, during tests, override that class.
Say your factory is in com.yourapp.factories.IntentsFactory and it is something like this:
public class IntentsFactory {
public static Intent getAccountPickerIntent (Context context) {
return AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null);
}
}
You should create in your test app (say it is com.yourapp.tests) a package with the same name and methods, but that returns a different Intent, a mocked/dummy one:
public class IntentsFactory {
public static Intent getAccountPickerIntent (Context context) {
return new Intent(context, MyDummyAccountPickerActivity.class);
}
}
Whenever your tests execute, they will use the "nearest" class in the classpath, that is, the IntentsFactory from your tests. Instead of returning an intent that send the flow to another app, the flow will go to an class of your project and Espresso won't end the tests.
The only caveat here is that you'll have to create the MyDummyAccountPickerActivity which will return a result and a Bundle similar to the one returned by the framework class. The activity should exists in your app's manifest and you'll have to instruct your emulator Dalvik runtime to allow classpath (check it out this this and this links) substitution with the following command line:
adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
adb shell stop installd
adb shell start installd
And execute your tests.
I had a similar problem to test the Camera and it's thoroughly discussed in Espresso forum
Seems, that you must operate on a root view which in your case the "account picker". Try this out:
public void testAccountPicker(){
onView(withText("Choose an account"))
.inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
.check(matches(isDisplayed()));
}
There are several ways that you might be able to go about testing this using Espresso Intents https://google.github.io/android-testing-support-library/docs/espresso/intents/
You can verify that the Intent was sent to open the account picker by using the intended() syntax. You can also verify the behavior of your activity with the returned result from the picker using the intending().respondWith() syntax.
If you really want to interact with the picker directly, you may be able to using the UIAutomator API: https://developer.android.com/topic/libraries/testing-support-library/index.html#UIAutomator
UIAutomator can be used inside of Espresso tests.
I'm trying to do Android unit testing for the first tme and I encounter a problem I can't seem to solve : only one of my test classes is ran, I'm not able to run test classes related to Activity testing, and even asserting true=false in them doesn't display an error.
My testing project is composed of three source files :
A test file for a class in my project (subclass of AndroidTestCase)
A test file for my first activity, LoginActivity (subclass of ActivityInstrumentationTestCase2)
A test file for another activity, EditUserActivity (once again subclass of ActivityInstrumentationTestCase2)
I used the following tutorial : http://forum.frandroid.com/topic/13831-traduc-de-tuto-les-tests-unitaires/ (in French but the code is in English)
And first read the following answer on StackOverflow : Trying to run Android JUnit tests in Eclipse fails? however it doesn't seems to be my problem
The code for the last test class is the following :
package com.imci.ica.test;
import com.imci.ica.EditUserActivity;
import android.test.ActivityInstrumentationTestCase2;
public class EditUserActivityTest extends
ActivityInstrumentationTestCase2<EditUserActivity> {
EditUserActivity mActivity;
public EditUserActivityTest() {
super("com.imci.ica", EditUserActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
mActivity = this.getActivity();
}
public void testTest() {
assertEquals(true, false);
}
}
Thanks in advance for your help!
I don't understand why, but I had to move the Eclipse project's files, so I closed the project, moved them and imported the project back, and now all the tests are checked, so my problem's fixed. If it can help somebody...
For me, I found that one testing class was crashing. I forgot to added non-argument constructor. Fixing that, all tests are run.
I have writing an Android JUnit test for an activity in my application. The Activity is modal, and can be configured via the launching intent.
I would like to write test methods to test the different modes. This would involve a setActivityIntent call in the setup method, configuring the Activity based on the test case we are running.
My question is, how can I determine which test case is about to be run from the setup method?
It turns out the answer to this question is very easy. junit.framework.TestCase has a getName method that returns the name of the current test case. Perfect.
In Junit 4.X, you can do the samething with TestName class:
public class MyTest {
#Rule public TestName name = new TestName();
#Before
public void before() {
System.out.println("running...", name.getMethodName());
}
}