Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Activity and Fragment Examples #22

Open
jaredsburrows opened this issue Jun 18, 2016 · 9 comments
Open

Add Activity and Fragment Examples #22

jaredsburrows opened this issue Jun 18, 2016 · 9 comments

Comments

@jaredsburrows
Copy link

No description provided.

@bjoernQ
Copy link
Owner

bjoernQ commented Jun 18, 2016

Hi,

what special problems are you facing when testing activities and fragments with Android Unit Tests/PowerMock/UnMock ?

In general you can create an instance of the class under test in your test code and call the appropriate callbacks before calling the code you want to test. The difficult thing is to mock everything that is needed to fully run the code but that very much depends on your actual code.

Bjoern

@jaredsburrows
Copy link
Author

@bjoernQ
Copy link
Owner

bjoernQ commented Jun 18, 2016

I'll have a look into that. Just to double check: the objective is to get rid of Robolectric and just use Android's plain Unit Test + PowerMock(ito) + UnMock plugin?

We did the same with a closed source project successfully before.

@jaredsburrows
Copy link
Author

@bjoernQ Exactly. I love Robolectric but sometimes memory usage and missing shadows can cause issues in the unit tests. It would be nice just to unit test against the real android jar.

I like using Mockito vs PowerMock.

Also, can your post a snippet of an Activity test from that closed source project?

@bjoernQ
Copy link
Owner

bjoernQ commented Jun 20, 2016

I had a look into the MainActivityTest. Since I am used to PowerMock(ito) I used that to mock things - seems you prefer the EasyMock API - but this should show the basic idea

@RunWith(PowerMockRunner.class)
@PrepareForTest(MainActivity.class)
public class MainActivityTest {

    @Test
    public void testNotNull() throws Exception {
        PowerMockito.suppress(MemberMatcher.methodsDeclaredIn(AppCompatActivity.class));
        MainActivity mainActivity = spy(new MainActivity());

        FragmentManager mockFragmentManager = mock(FragmentManager.class);
        when(mainActivity.getSupportFragmentManager()).thenReturn(mockFragmentManager);

        FragmentTransaction mockTransaction = mock(FragmentTransaction.class);
        when(mockFragmentManager.beginTransaction()).thenReturn(mockTransaction);

        when(mockTransaction.add(anyInt(), any(Fragment.class))).thenReturn(mockTransaction);

        mainActivity.onCreate(null);
        Whitebox.invokeMethod(mainActivity, "onStart");
        Whitebox.invokeMethod(mainActivity, "onResume");
        Whitebox.invokeMethod(mainActivity, "onPause");
        Whitebox.invokeMethod(mainActivity, "onDestroy");

        assertThat(mainActivity, not(nullValue()));
    }

   ....

For this to work you also need to set some test options

    testOptions {
        unitTests.returnDefaultValues = true
    }

For this simple scenario you don't even need the UnMock plugin. It's just Android's Unit Test + PowerMock(ito).

The test is still missing the verification code to make sure the fragment has been added (or not if the passed bundle is non-null).

But I hope it sketches the general idea. A lot more setup is needed without Robolectric but I personally prefer it this way. ( While I am really impressed by all the work done by the Robolectric devs - it's just a very different approach)

@jaredsburrows
Copy link
Author

@bjoernQ Thanks for the example!

Why do you have to mock anything if we are using the unmock plugin? Isn't that the point of this plugin so we do not have to mock anything like Robolectric?

Are you mocking as a way just to invoke the methods via Whitebox.invokeMethod?

Also, why use?

    testOptions {
        unitTests.returnDefaultValues = true
    }

@bjoernQ
Copy link
Owner

bjoernQ commented Jun 26, 2016

UnMock basically copies the original classes into the classpath (besides doing some class file transformation magic). That works for a lot of classes - e.g. TextUtils, SparseArray .....

It doesn't work for framework classes that rely on a lot of Android internals.

That's the main difference to Robolectric - Robolectric tries to emulate Android. The idea with UnMock is that you have much more control over what is mocked and how. Which needs quite a lot of setup compared to Robolectric.

It works best in an architecture where application logic is more decoupled from the Android framework while not trying to abstract everything that Android offers. (e.g. I prefer the PresentationModel ( http://martinfowler.com/eaaDev/PresentationModel.html ) for abstracting presentation logic which works like a charm also for testing and IMHO suits Android very well).

IMHO it's much easier to start with Robolectric but my decision to not use it was

  • it doesn't work well with PowerMock (which is needed in most cases when adding tests to legacy code)
  • it is hard to write truly isolated tests (e.g. when I tried it last time it fired up broadcast listeners when the tested code sent a broadcast - which might be what you want but in our case it wasn't since we wanted truly isolated tests without too much "coverage noise" (code reported as covered by tests which wasn't really verified in the test execution) )
  • it's not really fast in test execution (since it needs to setup a lot of stuff) which can be a show stopper when running thousands of tests during CI builds

So just taking the real implementations of Activity / FragmentManager won't work in a desktop JVM environment. Making it all work out of the box would result in a rebuild of Robolectric from scratch. But in a real project we use helper classes which setup proper / suitable mocking for all these things and the tests don't contain all the mock setup boiler plate code.

The testOption is just there to not have to mock really everything. i.e. it makes the stubs return null/zero instead of throwing a "StubException".

I was thinking of a "best practice" example project explaining all these things but unfortunately haven't had the time yet. ( And finding an example that is not too complex but complex enough is the hardest part of it for me )

Hope this helps and makes it more clear what UnMock is and what it not is (and never should be).

@jaredsburrows
Copy link
Author

jaredsburrows commented Jun 28, 2016

@bjoernQ Once again, thank you for your explanation! Maybe we can start with more pull requests to add more examples?

@jaredsburrows
Copy link
Author

@bjoernQ There are still no examples here: https://github.com/bjoernQ/unmock-plugin/tree/master/example/src/test/java/de/mobilej/testproject. I believe with an Activity and Fragment example, this will gain more adoption.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants