I'm using Android Studio AI-141.1972460 with JUnit 4 and am trying to write a simple JUnit test for an Activity:
public class GameActivityTest extends ActivityUnitTestCase<GameActivity> {
public GameActivityTest() {
super(GameActivity.class);
}
#Before
public void setUp() throws Exception {
super.setUp();
Intent intent = new Intent(getInstrumentation().getTargetContext(), GameActivity.class);
startActivity(intent, null, null);
. . .
The code compiles but getInstrumentation is returning null so I'm getting a NullPointerException there.
I tried using ActivityInstrumentationTestCase2 instead of this approach, but getActivity is likewise returning null in that case, too.
I assume this is somehow related to mocking? I have
testOptions {
unitTests.returnDefaultValues = true
}
in my build.gradle file as well as
testCompile 'org.mockito:mockito-core:1.9.5'
Do I need to mock out an Application object or something like that?
Thanks for your help.
Looks like I was trying to run my test as a "JUnit Test" (per the instructions at http://tools.android.com/tech-docs/unit-testing-support) and not as an "Android Test".
When I set it up as an "Android Instrumentation Test" (the difference being the Test Artifact setting in the Build Variants), this worked okay.
As a "JUnit Test", I was able to work around this and get my Activity object set up using Robolectric.
Related
I have a testsuite which has mutiple testcases in a class
every test case is isolated
So when i execute the testsuite class i want to restart the app for every testcase
How do i relaunch application from start for every individual test case in Espresso
Thanks in advance
#Test
public void testcase1() {
//from first screen
}
#Test
public void testcase2() {
//from first screen
}
There is another stack overflow answer that seems to answer this question. If you were looking to do that in Kotlin though I converted the answer to relaunch multiple times for different tests.
#RunWith(AndroidJUnit4::class)
class ExampleEspressoTest {
#get:Rule
val rule = ActivityTestRule(
activityClass = MainActivity::class.java,
initialTouchMode = false,
launchActivity = false) //set to false to customize intent
#Test
fun testCustomIntent() {
val intent = Intent().apply {
putExtra("your_key", "your_value")
}
rule.launchActivity(intent)
//continue with your test
}
}
If you need to start a method/test and when it's finished clear data and start the next one, you should use commands.
Look at this documentation: https://developer.android.com/studio/test/command-line
I'm using this command:
./gradlew testVariantNameUnitTest --tests *.sampleTestMethod
There could be several ways to do this but we wanted a way that works both locally as well as google fire base test lab, so ended up with using configuration in build.gradle file under default config.
defaultConfig{
testInstrumentationRunnerArguments clearPackageData: 'true'
}
Reference: https://developer.android.com/training/testing/junit-runner#ato-gradle
Also you use these runner arguments for configuring different tests you wanted run based on build variants or other config options, look at my post if you want more detail.
I have Android instrumentation tests with Espresso. Some of my tests must be run on an emulator - due to using LinkedIn's TestButler (https://github.com/linkedin/test-butler) library. This library toggles wifi/gsm for specific test runs, and that is why these tests must be run on an emulator.
My question is - can I annotate any specific tests to run on an emulator, while having the other tests run on a real device?
Thanks
Yes, you can use a #ConditionalIgnore annotation as described in http://www.codeaffine.com/2013/11/18/a-junit-rule-to-conditionally-ignore-tests/.
You will have something like
public class SomeTest {
#Rule
public ConditionalIgnoreRule rule = new ConditionalIgnoreRule();
#Test
#ConditionalIgnore( condition = NotRunningOnEmulator.class )
public void testSomething() {
// ...
}
}
public class NotRunningOnEmulator implements IgnoreCondition {
public boolean isSatisfied() {
return !Build.PRODUCT.startsWith("sdk_google");
}
}
EDIT
For this specific case of detecting a device or emulator you can also use #RequiresDevice.
The most straightforward solution I found is to use JUnit Assume API: http://junit.org/junit4/javadoc/4.12/org/junit/Assume.html
So, inside the test methods that can only be run on an emulator, I put this code:
Assume.assumeTrue("This test must be run in an emulator!", Build.PRODUCT.startsWith("sdk_google"));
This results in the said test being ignored when it isn't run on an emulator, and a handy error msg in the run window:
As you can see, the other two tests passed fine (in the green), and the entire test suite was able to run.
I am doing the unit testing on android studio, I create a test case in test class and on every time when I run that it give different output but when I debug it give me fine out put.It may be problem of caching or something else. I have also tried Invalidate studio and clean project but still occurring. Please Help.
In your #Before method, make sure to use Intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) to make sure the app is not already running in the background of your test when you begin testing. This could be what is introducing your cache issue.
to clarify, your #Before method should look something like this:
#Before
public void setup() {
//Initialize UiDevice instance
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(instrumentation);
mDevice.pressHome();
Intent intent = new Intent("com.REDACTED.auto.diagnostics.dealer.MAIN");
intent.setClassName("com.REDACTED.auto.diagnostics", "com.REDACTED.auto.diagnostics.dealer.MainActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Context context = InstrumentationRegistry.getContext();
context.startActivity(intent);
}
There are simpler ways to do this if you have the source code, however in my case I did not
I am working on implementing automated unit tests for our application. Want i need to do is be able to choose from a list of test cases and execute said test. My initial idea was to create (1) a standalone application that references a (2) testing library (android project with a bunch of unit tests) that would invoke the activities in our (3) application. So our application of course works fine and I don't want to make any changes there if possible. The standalone application would server as an interface to us developers for executing the tests. I want to be able to add more and more tests to the library. The issue I am having at this point is what follows.
In the library project I have a basic LoginTest activity.
public class LoginTest extends InstrumentationTestCase
{
... other class code
#Override
protected void setUp()
{
solo = new Solo(getInstrumentation());
}
}
My issue is the getInstrumentation() call always returns null. In my library manifest I have
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.matrix.edc.client.android" />
Make sure you have the robotium jar installed. The Solo class belongs to the robotium library.
If you have that installed and I've insulted your intelligence try changing your class declaration to
public class LoginTest extends ActivityInstrumentationTestCase2<YourMainActivity>
and change
solo = new Solo(getInstrumentation, getActivity());
in setUp().
You should also make sure, to call super.setUp() before any attempt of getting the instrumentation. I have not tested every subclass, but for ActivityInstrumentationTestCase2:
#Override
protected void setUp()
{
getInstrumentation(); // returns null
super.setUp()
getInstrumentation(); // returns valid instance
}
trying injecting the instrumentation in your setup, like so
public void setUp() throws Exception
{
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
solo = new Solo(getInstrumentation(), getActivity());
}
I run my Android tests by running a test implementation which "derives" from a library project (because I have a multi module project with baselib and "concrete app projects"). The test implementation is one of these concrete app projects and is launched by an InstrumentationTestCase. In this test case I mock several parts from the library project by RoboGuice. That means I run a "real" implementation of my baselib with mocked classes (like persistence handling, database handling and so on). To be able to do that, every single test case has to close and restart the whole test instance, because I can't start the same app twice on the device. These test are more integration tests than Junit tests, because I test some kind of workflows, but there is no other possibility to test that, because the possibilities with JUnit on Android testing seem to be very limited.
At the moment I can only run one test case at the same time, because if I run more than 1, the whole test is hanging. I already checked if it's the configuration change (see private method) which causes my test to freeze, but this is not the cause. See my attempts in tearDown method. I can't run
getInstrumentation().finish(0, new Bundle());
because I get
Test failed to run to completion. Reason: 'Test run failed to
complete. Expected 3 tests, received 1'
I also cannot run
getInstrumentation().callActivityOnDestroy(activity);
because I don't have an Activity here. Moreover the Activity "StartTestActivity" which is launched at startup is not the same Activity which runs when the test is finished because StartTestActivity launches another Activity "MainMenuActivity" which is running at the end of the test. I already thought about using Instrumentation.ActivityMonitor but this doesn't provide the needed functionality.
Nevertheless I want to somehow start with the same test conditions at every test case as the whole test itself does at startup, but I'm not sure what InstrumentationTestCase is doing in the background, so I don't know how to restart the whole instrumentation setup. I somehow need to stop and restart the test instance, or maybe there is a better solution? Any ideas?
(by the way: every test itself runs fine, so it's no problem of the test ifself).
public class WorkflowModule1Test extends InstrumentationTestCase
{
private PersistenceManagerMock persistenceManager;
#Override
protected void setUp() throws Exception
{
super.setUp();
}
#Override
protected void tearDown() throws Exception
{
super.tearDown();
if (persistenceManager != null)
{
persistenceManager.clear();
}
}
public void testSaveLocaleEN() throws PersistenceException
{
updateLocaleConfiguration(Locale.ENGLISH);
Intent intent = new Intent(getInstrumentation().getContext(), StartTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getInstrumentation().startActivitySync(intent);
persistenceManager = (PersistenceManagerMock)RoboGuice.getInjector(ContextProvider.getApplication()).getInstance(IPersistenceManager.class);
List<Entity> entities = persistenceManager.getEntities();
assertTrue(entities.size() == 1);
assertTrue(entities.get(0) instanceof LanguageUsageRel);
assertTrue(((LanguageUsageRel)entities.get(0)).getLanguageId().equals("EN"));
}
public void testSaveLocaleDE() throws PersistenceException
{
updateLocaleConfiguration(Locale.GERMAN);
Intent intent = new Intent(getInstrumentation().getContext(), StartTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getInstrumentation().startActivitySync(intent);
persistenceManager = (PersistenceManagerMock)RoboGuice.getInjector(ContextProvider.getApplication()).getInstance(IPersistenceManager.class);
List<Entity> entities = persistenceManager.getEntities();
assertTrue(entities.size() == 1);
assertTrue(entities.get(0) instanceof LanguageUsageRel);
assertTrue(((LanguageUsageRel)entities.get(0)).getLanguageId().equals("DE"));
}
private void updateLocaleConfiguration(Locale locale)
{
Locale.setDefault(locale);
Configuration configuration = new Configuration();
configuration.locale = locale;
getInstrumentation().getContext().getResources().updateConfiguration(configuration, getInstrumentation().getContext().getResources().getDisplayMetrics());
}
}
I think if you extended ActivityInstrumentationTestCase2 instead this would solve a lot of your problems.
Another note: Put your tear down logic before the super.tearDown() call.
I found the solution on my own. I have to set these two flags.
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);