Android InstrumentationTestCase getFilesDir() returns null - android

I am using InstrumentationTestCase to unit test a component of my application.
The component persists data to the internal storage and uses Context::fileList(); to retrieve the persisted files.
I experience the following problem: Using this method in the app (on the device) works perfectly fine. But when I try to (Android-)Unit-Test (also on the Device) with use of InstrumentationTestCase I get a NullPointerException inside the fileList() method. I digged into the android source and found out that getFilesDir() (see source here) returns null and causes this error.
The code to reproduce is the following:
public class MyTestCase extends InstrumentationTestCase
{
public void testExample() throws Exception
{
assertNotNull(getInstrumentation().getContext().getFilesDir()); // Fails
}
}
My questions are: Is this behaviour intended? What can I do to circumvent this issue? Am I using InstrumentationTestCase right or should I use something different?
I found this question but I'm not sure if this covers the same problem I have.

I think that you are right with keeping your test data separate from tested application.
You can fix problem with Null by creating files directory for Instrumentation app by executing the following commands
adb shell
cd /data/data/<package_id_of_instrumentation_app>
mkdir files
You can do above only on emulator or rooted device.
Then test from your question will not fail. I did it and also uploaded file named tst.txt to files dir, all below tests were successful:
assertNotNull(getInstrumentation().getContext().getFilesDir());
assertNotNull(getInstrumentation().getContext().openFileInput("tst.txt"));
assertNotNull(getInstrumentation().getContext().openFileOutput("out.txt", Context.MODE_PRIVATE));
But I think more convenient way to provide data to test project is to use assets of test project where you can simply save some files and open them:
assertNotNull(getInstrumentation().getContext().getAssets().open("asset.txt"));
or if you want to save some results of tests to the file you can use ExternalStorage:
File extStorage = Environment.getExternalStorageDirectory();
assertNotNull(extStorage);

#Blackbelt mentioned in his comment to use getTargetContext() instead of getContext(). I missed the comment, and after a few hours, of trying to figure out how to Realm.init() from an Android instrumented tests, I find out that I need the Context from getTargetContext()...(along the way, i tried to context.filesDir.mkdirs())
package com.github.ericytsang
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Test
class InstrumentedTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
#Test
fun can_mkdirs() {
assert(context.filesDir.mkdirs())
}
}

Related

How to use android.os.Process in junit test?

I'm trying to implement a simple test that uses android.os.Process.myPid (actually I have a large class that uses myPid, but to avoid posting large source I simplified everything down to this):
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class ProcessTest {
#Test
public void testTest() {
assertTrue(true);
}
#Test
public void testPid() {
int pid = android.os.Process.myPid();
//assertTrue(true);
}
}
First test passes ok, but second fails with this error message:
java.lang.UnsatisfiedLinkError: android.os.Process.myPid()I
What's wrong? How to test classes that use android.os.Process?
You can't do that in unit tests. You can't access Android OS packages in unit tests. You can mock them! You want a process id, but there's no OS running (apart from the machine you're developing on).
After a while I found solution for my problem -- instrumentation tests. Well, yes, as Krzysztof Kubicki pointed out, it can't be done in unit tests. But while it's possible to mock things, is difficult in real case. I was able to mock pid for my simple test, but the real thing was AesCbcWithIntegrity by Tozny with all bells and whistles, including things like this:
int bytesRead = (Integer) Class
.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_load_file", String.class, long.class)
.invoke(null, "/dev/urandom", 1024);
This is kinda difficult to mock. So, instead of mocking I created instrumentation test and ran tests on emulator.
And that solved my problem without much effort. Yes, instrumentation tests are slow compared to regular unit testing, but at least it works.

cn1 - download file to phone's download directory

I'm trying to allow an app to download files to the public 'Downloads' directory so it's available on the device in a generic fashion. I succeeded in downloading the files in the simulator to the .cn1 directory on my computer, but I couldn't find a straightforward way to get the file into a public directory on the device. I really expected there to be a method in the FileSystemStorage class that would allow this, but none of them seem to be what I'm looking for.
So I tried writing a simple native bridge to get the path to the public directory, starting with Android. I have a very simple class that looks like this:
public class DownloadDirectoryImpl {
public static String getDownloadDirectory(){
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
}
public boolean isSupported(){
return true;
}
}
The app compiles fine, but when I click on the file to download it, I see the same line repeating itself in the logcat a few thousand times:
W/System.err: at net.gesher.downloadDirectory.NativeDownloadDirectoryImpl.getDeviceDownloadDirectory(NativeDownloadDirectoryImpl.java:20)
But it doesn't state what the error is.
So, I'm looking for advice either a) to improve my android code so that it works, or b) the cn1 proper way of getting this directory path.
Thanks a ton!
If you have a lot of lines saying at... and all refer to the same method then you have a recursive call leading to a stack overflow. Since the only method you mentioned is your native method I'm assuming you called your own method within the native implementation and got into a recursive loop.

File storage with multiple build flavours

In my application I make use of two build flavours / build variants. After switching to two build flavours, a bug was introduced in my application. I have now discovered the reason for this bug, but I am unable to find a solution.
The situation:
In my MainActivity class, I have a function that checks if a file exists - it is very straightforward;
public boolean fileExists(String filename) {
File file = null;
file = this.getApplicationContext().getFileStreamPath(filename);
return file.exists();
}
Using the debugger, location of the file is reported as: /data/data/foo.bar.appname.buildflavour/files/filename
In another class, I try to write to this same location;
outputStream = getActivity().getApplicationContext().openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write("test");
outputStream.close();
However, when I print the following line in front of the outputStream getActivity().getApplicationContext() - context is reported as; com.foo.bar.appname#14fcdd18. Therefore, I believe that these two classes are trying to save / retrieve a file in different locations. Any ideas on how I can make sure that the application is writing the file in the correct build flavour location? Thank you in advance!
You need to use different application id.
Form official site Configuring Gradle Builds
When using build variants, the build system enables you to uniquely identify different packages for each product flavors and build types.
productFlavors {
pro {
applicationId = "com.example.my.pkg.pro"
}
free {
applicationId = "com.example.my.pkg.free"
}
}
Eventually I was able to find the answer to my question. Initially I was calling a function in MainActivity onCreate, but this function was no longer being called because the buildFlavors redirected directly to one of my application fragments.
To anyone coming here with a similar problem: verify that your Flavors call the necessary functions and classes.

Android test annotations with Robotium

I'm currently building an app in Android, and using Robotium to do functional tests (By the way, don't use Robotium on anything less that Android 1.6, it is way too buggy).
Some of these tests have a random tendency to fail, mainly Robotium missing a text field, or timing out, not reading text. I am trying to use the #FlakyTest annotation, so they will run two or three times before throwing out a failed test error. However, the annotation is not working, the tests do not re-run after a failure.
Here is how I am using the annotation:
public class ClassName extends ActivityInstrumentationTestCase2<HomeActivity>{
#LargeTest
#FlakyTest(tolerance=3)
public void testMethod(){
//Here I run my roboitium scripts.
}
}
Then I run it from the command line:
adb shell am instrument -w com.jayway.test/android.test.InstrumentationTestRunner
Neither eclipse nor the command line execution of the tests takes into account the flaky test annotation. Does anyone see an error with how I am trying to apply #FlakyTest?
I can't see any issue with your use of the #FlakyTest annotation.
I put together a quick test case to test #FlakyTest and Robotium (v2.2):
public class FlakyTestCase extends ActivityInstrumentationTestCase2<Main> {
private static int count = 0;
private Solo solo;
public FlakyTestCase() {
super("com.stackoverflow.example", Main.class);
}
#Override
public void setUp() throws Exception {
solo = new Solo(getInstrumentation(), getActivity());
}
#LargeTest
#FlakyTest(tolerance=3)
public void testFlaky(){
Log.e("FlakeyTestCase", "Execution Count:" + ++count);
solo.assertCurrentActivity(null,Main.class);
solo.clickOnText("Doesn't Exist");
Log.e("FlakeyTestCase", "Shouldn't make it here");
}
}
LogCat showed the following messages:
Execution Count: 1
Execution Count: 2
Execution Count: 3
So the #FlakyTest annotation was definitely being invoked. The (final) failure of the test was shown as:
junit.framework.AssertionFailedError: The text: Doesn't Exist is not found!
And the message "Shouldn't make it here" was never logged.
So as far as I can see, there is no issue with how you've declared your annotation or any problems with #FlakyTest and Robotium, v2.2 anyway.
Perhaps there is an issue with another part of your test code?
In general, when writing tests for Android (with or without Robotium) you have to be much more careful. You can't just say "is this visible". You need to wrap everything in a "wait for" cycle, so would say "wait for this to be visible". This is particularly a problem when running in the emulators, because sometimes things take long without any good reason. Without the waiting cycles, you will never have a consistent run. We have a few hundred tests and we have never needed to use the FlakyTest annotation.
Robotium missing a text field, or timing out, not reading text means
We have to check clearly if the text or any existed on the screen then only need to perform the actions like
if(solo.searchText("Doesn't Exist", true){
solo.clickOnText("Doesn't Exist");
}
Similar if any components like button or others we can achieve this by above logic.
Add this to your code:
import android.util.Log;

Problems with running Android Activity unit-testing from Eclipse

Im having a problem starting or running any activity unit tests from within eclipse.
Even i start a clean project and make a simple test class it always prints to the console:
[2010-10-05 13:10:24 - testAndroid] Collecting test information
[2010-10-05 13:10:25 - testAndroid] Test run failed: Test run incomplete. Expected 2 tests, received 0
Any ideas ?
Just for testing, I have created a fresh Android project called Demo with a test project called DemoTest
The main activity to test is called Main and I have created a simple testclass MainTest that looks like this:
package net.demo.test;
import android.test.ActivityInstrumentationTestCase2;
import net.demo.Main;
public class MainTest extends ActivityInstrumentationTestCase2<Main>
{
public MainTest()
{
super("net.demo", Main.class);
// TODO Auto-generated constructor stub
}
}
My tests used to run fine before, but suddenly I cant run any of them, they all fail with the same error, even I create new a project. It seems like it something to do with Eclipse or and not with the Code.
Update:
Seems like extending SingleLaunchActivityTestCase<Main> is working, but still got no clue about how to make ActivityInstrumentationTestCase2<Main> working.
I had no regression problems. I just couldn't get the example to work. I finally fixed it by defining two constructors:
public MainActivityTest(String pkg, Class<MainActivity> activityClass) {
super("com.myapp", MainActivity.class);
}
public MainActivityTest() {
super("com.myapp", MainActivity.class);
}
It turned out that most emulators before 2.3.3 were silently swallowing the error generated when construction went wrong.
You must put at least 2 methods (i.e 2 test cases) into the Test class. even methods without definition inside can do the trick

Categories

Resources