I'm currently unit-testing a library. In certain classes, I need to pass an activity as parameter in some static methods. The library itself contains no activities. I need to somehow obtain an instance of a mock activity to use in each individual method test.
I've already read the Activity Testing Tutorial and the Testing Fundamentals section. Most of what it is said there only makes sense if you are about to test Activities already existing in the project to be tested. But I just need a mock one to do things like showing dialogs and run short tasks in the Ui thread.
What would the fastest, simplest way to achieve this? Should I create the mock activity in my test project and also provide xml layout resources for the dummy UI?
UPDATE
Since I've not found any way of creating a mock activity automatically I decided to provide it by myself. I've created inside the test project a dummy activity doing nothing and provided a dummy layout via xml. Then I coded my test extending ActivityInstrumentationTestCase2
:
public class LibraryTest extends ActivityInstrumentationTestCase2<MockActivity> {
public LibraryTest(String name) {
super(MockActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
}
public void testAMethodFromLibrary() {
fail("Not yet implemented");
}
}
Where MockActivity
is the aforementioned mock activity I've created in this test project. However, seems that the Android test framework is having trouble launching the activity and it comes up with this exception:
java.lang.RuntimeException: Exception during suite construction
at android.test.suitebuilder.TestSuiteBuilder$FailedToCreateTests.testSuiteConstructionFailed(TestSuiteBuilder.java:239)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
Caused by: java.lang.NullPointerException: Method name must not be null.
at java.lang.ClassCache.findMethodByName(ClassCache.java:297)
at java.lang.Class.getMethod(Class.java:985)
at android.test.suitebuilder.TestMethod.getAnnotation(TestMethod.java:60)
at android.test.suitebuilder.annotation.HasMethodAnnotation.apply(HasMethodAnnotation.java:39)
at android.test.suitebuilder.annotation.HasMethodAnnotation.apply(HasMethodAnnotation.java:30)
at com.android.internal.util.Predicates$OrPredicate.apply(Predicates.java:106)
at android.test.suitebuilder.annotation.HasAnnotation.apply(HasAnnotation.java:42)
at android.test.suitebuilder.annotation.HasAnnotation.apply(HasAnnotation.java:31)
at com.android.internal.util.Predicates$NotPredicate.apply(Predicates.java:122)
at android.test.suitebuilder.TestSuiteBuilder.satisfiesAllPredicates(TestSuiteBuilder.java:254)
at android.test.suitebuilder.TestSuiteBuilder.build(TestSuiteBuilder.java:190)
at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:373)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4218)
at android.app.ActivityThread.access$3000(ActivityThread.java:125)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2071)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Now I'm totally lost here. How can this be so complicated? Have I chosen the correct way? I just want to launch a dialog in a test method. Maybe the framework is messing up because the activity to test is not in the target project?
Any help here would be much appreciated. I'm getting out of time and if I can't find the proper way to do this I'd have to create a second project using my library, move the mock activity there and test from the test project. This is a lot of code because I'd have to include a method in a (now not generic) mock activity just to call every library method I want to test.
Mocks can produce better tests by reducing a factor that could cause the test to fail (e.g. needing to access an external resource like a database, which could be offline for reasons unrelated to your test) and helping you isolate the classes being tested.
To test an activity, you use the ActivityTestRule class provided by the Android Testing Support Library. This rule provides functional testing of a single activity. The activity under test will be launched before each test annotated with @Test and before any method annotated with @Before.
Mocking is done when you invoke methods of a class that has external communication like database calls or rest calls. Through mocking you can explicitly define the return value of methods without actually executing the steps of the method.
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
With androidX libraries, you can use ActivityScenario
.
Simply import androidTestImplementation("androidx.test:core:1.2.0")
in your app.gradle file
Then, in your instrumented test, import ActivityScenario
and launch the activity with:
import androidx.test.core.app.ActivityScenario
@Test
fun testActivity() {
ActivityScenario.launch(MainActivity::class.java).onActivity { activity ->
// do something with your activity instance
}
}
Solved! Here's what I did:
To solve the exception I posted above, just replaced the test case constructor with this one:
public LibraryTest() {
super(MockActivity.class);
}
Now that works and I can successfully launch dialogs. But in my brief research I stumbled upon Robotium. This library is just amazing. It is not needed for what I was trying to do at first, but I found it very useful to test GUIs in an automated way. Now I have a fresh new activity recreated in each setUp
call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With