How to perform #After for a specific JUnit test case? - android

I'm using Espresso to build UI tests. For some test cases I would like to call a specific after step to reset the state in case the script fails.
Is there a way to perform an #After step for a single JUnit test case (#Test)?
The only solution I can think of is to make a separate test class. But I would like the test cases to be grouped in the same test class.

It does sound a little odd ;) but ...
You could add a try/finally to the single test for which you want this after behaviour. For example:
#Test
public void testA() {
try {
// the body of testA
} finally {
// apply the 'after' behaviour for testA
}
}
Or, if you really want to use JUnit's #After then you could use the TestName Rule (since JUnit 4.7) as follows:
#Rule
public TestName testName = new TestName();
#After
public void conditionalAfter() {
if ("testB".equals(testName.getMethodName())) {
System.out.println("apply the 'after' behaviour for testB");
}
}
#Test
public void testA() {
}
#Test
public void testB() {
}

Related

How to test element of custom sdk android unit test

I've started learning android unit tests, but it looks very hard to find some good guides or information. Every example have a stupid example about 2+2 = 4
Say I write a little SDK which has few functions
MySdk.Init(Context context)
MySdk.CallTask()
I create an androidTest file
How should I call my SDK functions to check how they work? Somewhere required parameters like int/string/context. I just really don't understand, please help me.
This is what I've tried
public class AndroidTest {
private Activity context;
//default test
#Test
public void addition_correct() throws Exception {
assertEquals(4, 2 + 2);
}
#Test
public void checkContext() {
context = getActivity();
assertNotNull(context);
}
#Test
public void testInitPhase() {
MySdk.Init(context, new SdkInitializationListener() {
#Override
public void onInitializationSuccessful(String adv_id) {
assert (adv_id != null);
}
#Override
public void onInitializationError() {
}
});
}
}
For context i was tried context = new mockContext();. It's passed as context = null and my SDK failed with initialization.
Unit tests are mainly about testing an individual class in isolation, so that you can check if individual public methods of a class behave as you intend them to, and continue to do so if you change that class' code in the future. Let's say you have a class like this:
public class UtilityFunctions {
public int double(int value) {
return value * 2;
}
public String mirror(String value) {
if (value == null) return "";
return value + new StringBuilder(value).reverse().toString();
}
}
You want to test these two methods with:
valid input values, and check the output is as expected
invalid values, and check that errors are handled accordingly (and the correct exceptions thrown if necessary)
So a test class for the above class may look like this
#RunWith(JUnit4.class)
public class UtilityFunctionsTest {
private UtilityFunctions utility;
#Before
public void setUp() {
// Initialises any conditions before each test
utility = new UtilityFunctions();
}
#Test
public void testDoubleFunction() {
assertEquals(2, utility.double(1));
assertEquals(8, utility.double(4));
assertEquals(-12, utility.double(-6));
assertEquals(0, utility.double(0));
}
#Test
public void testMirror() {
assertEquals("", utility.mirror(null));
assertEquals("", utility.mirror(""));
assertEquals("aa", utility.mirror("a"));
assertEquals("MirrorrorriM", utility.mirror("Mirror"));
}
}
These standard Java unit tests are run from the test directory. However, you'll need to run tests in the androidTest directory whenever you're using Android-specific classes such as Context. If you're creating a MockContext, you're simply creating an empty Context whose methods don't do anything.
Without me knowing anything about what your MySDK does, I think you may need to pass a fully-functioning Context into your class for your tests. The Android JUnit runner does provide this with InstrumentationRegistry.getTargetContext(), so for your example, you may need to add this #Before method:
#Before
public void setUp() {
context = InstrumentationRegistry.getTargetContext();
}
You'll also need to remove the context = getActivity(); line from your first test.

Toggling between multiple espresso tests on Android

I'm new to the automated testing, and using espresso to test my android App.
The problem is that I have multiple dynamic views depending on certain conditions :
My user has a boolean attribute, let's call it "isPremium"
when I click on a button my user is redirected to FragmentA if isPremuim == true , else he's redirected to FragmentB.
now for my tests I have
#Test public void testFragmentA();
and
#Test public void testFragmentB();
but when I run my tests based on my data, forcibly one of the two tests fails.
so should i make one test for both fragments like
private void testFragmentA();
private void testFragmentB();
#Test
public void myGlobalTest {
if(user.isPremium) testFragmentA();
else testFragmentB();
}
is this the right way to make my tests ? or there is another better way, because sincerly I'm not convinced with this method.
It would be best if you set value for premium at the beginning of each test (true for testFragmentA, false for testFragmentB). That way you will know what you are expecting and what each fragment depends on.
Also, if user is some global variable, you should keep its state in #Before and restore it in #After method.
boolean isPremium;
#Before
public void init() {
isPremium = User.isPremium();
}
#Test
public void testFragmentA(){
User.setPremium(true);
// test fragment A
}
#Test
public void testFragmentB(){
User.setPremium(false);
// test fragment B
}
#After
public void restore() {
User.setPremium(isPremium);
}

How to write unit test for RxJava CompositeSubscription

#Override public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(
interactor.getSearchResultByBarcode(barcode).subscribe(subscriberForSearchResults(true)));
}
}
private Subscriber<PriceAndStockActivityViewModel> subscriberForSearchResults(
boolean fromBarcode) {
return new BaseSubscriber<PriceAndStockActivityViewModel>() {
#Override public void onNext(PriceAndStockActivityViewModel priceAndStockActivityViewModel) {
super.onNext(priceAndStockActivityViewModel);
view.updateView(priceAndStockActivityViewModel);
}
#Override public void onError(Throwable e) {
super.onError(e);
view.hideProgress();
view.organizeScreenComponentsByVisibility(true);
view.onError(e);
}
};
}
I've wanted to test method called onBarcodeReceived like below
#Test public void should_updateViewByViewModel_when_AnyBarcodeReceived() {
String barcode = "123123123";
PriceAndStockActivityViewModel viewModel = getPriceAndStockActivityViewModel(barcode);
when(textUtil.isEmpty(barcode)).thenReturn(false);
when(interactor.getSearchResultByBarcode(anyString())).thenReturn(Observable.just(viewModel));
presenter.onBarcodeReceived(barcode);
verify(view).showProgress();
verify(interactor).getSearchResultByBarcode(anyString());
verify(view).updateView(any(PriceAndStockActivityViewModel.class));
}
Since onNext runs in a different thread its normal not to reach view.updateView. It looks simple but I couldn't find how to solve it. Is there any way to verify updateView?
I presume getSearchResultByBarcode() works on a background thread. So I wonder how you're able to change your UI from this background thread?
I'd change the execution of your subscriber to Android's main thread, so that you can safely manipulate the view, regardless if the thread of getSearchResultByBarcode() changes in the future. However will not hardcode the Scheduler directly, rather lets inject it in the presenter class, for example via the constructor. Of course when you're creating the "real" presenter, you'd pass in AndroidSchedulers.mainThread():
public MyPresenter(, Scheduler observeScheduler) {
...
this.observeScheduler = observeScheduler;
}
....
#Override
public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(interactor.getSearchResultByBarcode(barcode)
.observeOn(observeScheduler)
.subscribe(subscriberForSearchResults(true)));
}
}
Then in your test, when constructing the Presenter you'd use Schedulers.immediate() (if you're using RxJava 1.x or Schedulers.trampoline() if you're using RxJava 2.x version. That should work without using any timeout()s in your Unit tests with Mockito ... after all you want them to run as fast as possible.
And one unrelated thing - you can use org.apache.commons.lang3.StringUtils as a substitution of android.text.TextUtils - it has roughly the same functionality but you won't need to mock it in your unit tests.
In order to wait for another thread to complete you can use this Mockito feature: verify with timeout.
verify(view, timeout(100)).updateView(any(PriceAndStockActivityViewModel.class));
Or use some means of thread synchronization like CountDownLatch. See example for Mockito here.

Android Espresso Intents test randomly fail with ``init() must be called prior to using this method``

I am working on pushing a project into espresso testing currently. I have read a bunch of documents and follow the given practises to get started.
Everything works fine, However, when it comes to Intents related test, the result is strange.
Most of the time, the tests passed in my Mac but fail in my colleague's Windows(not all tests fail) with the the fail message java.lang.IllegalStateException: init() must be called prior to using this method.
Quite strangely, If we Run Debug test in Android Studio flow the code step by step, it passes.
here is the test code:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MainActivityTest {
#Rule public IntentsTestRule<MainActivity> mRule = new IntentsTestRule<>(MainActivity.class, true, false);
AccountManager accountManager;
MainActivity activity;
private void buildLoginStatus() throws AuthenticatorException {
DanteApp app = (DanteApp) InstrumentationRegistry.getTargetContext().getApplicationContext();
accountManager = app.getDanteAppComponent().accountManager();
DoctorModel doctorModel = AccountMocker.mockDoctorModel();
accountManager.save(doctorModel.doctor);
accountManager.setAccessToken(doctorModel.access_token, false);
}
#Before public void before() throws Exception {
buildLoginStatus();
// must login
assertThat(accountManager.hasAuthenticated(), is(true));
activity = mRule.launchActivity(null);
// block all of the outer intents
intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
}
#After public void tearDown() throws Exception {
accountManager.delete();
}
// failed
#Test public void testViewDisplay() throws Exception {
// check tabhost is displayed
onView(withClassName(equalTo(TabHost.class.getName()))).check(matches(isDisplayed()));
// check toolbar is displayed
onView(withClassName(equalTo(ToolBar.class.getName()))).check(matches(isDisplayed()));
}
// passed
#Test public void testCallServiceHotline() throws Exception {
// switch to the account tab layout
onView(withChild(withText(R.string.account))).perform(click());
// click account menu to make a service call
onView(withId(R.id.contact)).perform(click());
// check call start expectly
intended(allOf(
not(isInternal()),
hasAction(Intent.ACTION_DIAL),
hasData(Uri.parse("tel:" + activity.getString(R.string.call_service)))
));
}
// failed
#Test public void testOpenSettingsUI() throws Exception {
// stub all internal intents
Intents.intending(isInternal())
.respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
onView(withChild(withText(R.string.account))).perform(click());
onView(withId(R.id.setting)).perform(click());
// check open settings activity successfully
intended(anyOf(
hasComponent(SettingActivity.class.getName())
));
}
}
The testing library version(nearly all dependencies are up to date and we use both physics devices and emulator to test):
rule: 0.4.1
runner: 0.4.1
espresso-*: 2.2.1
support-*: 23.1.0
Any idea deserves an appreciation. Thanks!
Two Solutions:
Use ActivityTestRule instead of IntentsTestRule and then in your #Before and #After manually call Intents.init() and Intents.release() respectively.
Write a custom IntentTestRule and override beforeActivityLaunched() to include your AccountManager logic. Use afterActivityFinished for your current #After logic. This will also allow you to just use the default IntentTestRule constructor. (Preferred Solution)
As to why this is happening:
"Finally on an unrelated note, be careful when using the new IntentsTestRule. It does not initialize, Intents.init(), until after the activity is launched (afterActivityLaunched())." - Shameless plug to my own post (halfway down helpful visual)
I think you are running into a race condition where in your #Before method you are executing launchActivity() then espresso tries to execute intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null)); before your activity is actually created, which means afterActivityLaunched() isn't called, which means neither is Intents.init(), crash!
Hope this helps.
IntentsTestRule is derived from ActivityTestRule and should manage Intents.init() and Intents.release() for you.
However, in my case the IntentsTestRule did not work properly. So I switch back to ActivityTestRule and call Intents.init() before and Intents.release() after the test which sent the Intent.
For more information please see this reference.

Android - ActivityUnitTestCase - Tests Always Pass

I am using Android Studio to try and test my activity. Here is the basic code:
public class MyActivityTest extends ActivityUnitTestCase<MyActivity> {
public MyActivityTest() {
super(MyActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
}
#SmallTest
public void testSomething() {
Assert.assertNotNull("something is null", null);
}
}
I would expect that this test case fails. Everything I try passes though. This seems like a strange question, but how can I make my test case fail? What am I doing wrong?
I managed to get this working, sort of. I found this on a bug report:
We are in the process of deprecating ActivityUnitTestCase. We recommend to move business logic to a separate class and unit test it with gradle unit test support (mockable android.jar).
So I extended ActivityInstrumentationTestCase2 instead and ran the test as an Instrumentation Test rather than a Unit Test. That worked. Here is basically what I have now:
public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> {
public MyActivityTest() {
super(MyActivity.class);
}
public void testSomething() throws Exception {
//test goes here
Assert.assertEquals(message, expectedObject, actualObject);
}
}
I'm still not sure why I was seeing the behavior I was earlier, but at least I can test now. Here is a screenshot of my Test Build Configuration:

Categories

Resources