android studio 2.1. preview 4
I am creating a junit4 unit test to test for opening a file contained in the raw directory.
However, everytime the code runs I can a null pointer from openRawResource.
This is the function I am trying to test. This works when running on the actual device. But not in the unit test.
public String getNewsFeed(Context mContext) {
InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list); // Null pointer
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferReader = new BufferedReader(inputReader);
int n;
while ((n = bufferReader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
inputStream.close();
}
catch (IOException ioException) {
return "";
}
return writer.toString();
}
This is my test case
#RunWith(MockitoJUnitRunner.class)
public class NewsListPresenterTest {
#Mock
private Context mContext;
#Mock
private NewsListPresenter mNewsListPresenter;
#org.junit.Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mNewsListPresenter = new NewsListPresenter(mContext);
}
#org.junit.Test
public void testLoadNewsFeed() throws Exception {
assertNotNull(mNewsListPresenter.getNewsFeed(mContext));
}
}
Many thanks for any suggestions,
You are confusing the two types of unit tests in Android. This is not clear for many people so I'll explain it here.
Why it works on a device: Because this is an instrumented test.
What is an instrumented test? A test that runs on a real device/emulator and the test code is in the 'src/androidTest' folder.
Why it doesn't work as a local junit test: Because local junit tests are not instrumented tests. Local Junit tests run on your computer's JVM, not on the device. Local Junit tests shouldn't contain/use Android code because the real Android code is on the device/emulator, not on your computer's JVM.
I suppose you want to make it run as a junit test to run it faster, and that's why I suppose you moved your test to the 'src/test' folder and context.getResources() is throwing a NullPointerException.
I think you have 2 options here:
Use Robolectric to run this test as a junit test
Refactor your method so it doesn't depend on Android's classes
For option 2, this is what I would do. Change the method's argument to an InputStream:
public String getNewsFeed(InputStream inputStream) {... use inputStream... }
Now your method doesn't see any Android code and you can test it as a normal junit method. You could then pass a fake inputStream to your method, like this:
#Test
public void testLoadNewsFeed() throws Exception {
String fileContents = "line one";
InputStream inputStream = new ByteArrayInputStream(fileContents.getBytes());
assertNotNull(mNewsListPresenter.getNewsFeed(inputStream));
}
If you still want to pass the same inputStream as you're using in your app (I wouldn't recommend it) you still can do it using this code in the test:
#Test
public void testLoadNewsFeed() throws Exception {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("raw/news_list");
assertNotNull(mNewsListPresenter.getNewsFeed(inputStream));
}
And you'll have to add this line to your build.gradle file:
sourceSets.test.resources.srcDirs += ["src/main"]
Android unit tests can be very confusing. You can read about these concepts in this blog post that I wrote
You have to tell the mContext mock what to do when getResources() is called on it.
when(mContext.getResources()).thenReturn(some_mock_of_Resources);
If you don't specify anything, the mock will return null.
For your example, this means you'll probably also need a Resources mock, and also tell it when to do/return when openRawResource() is called on that.
You have created a mock Context so you have to stub the methods used by the method under test.
In getNewsFeed your are using Context::getResources so in your test before invocation of getNewsFeed, you should have:
Resources resoures = ...
when(mContext.getResources()).thenReturn(resources);
The Mockito documentation of stubbing is pretty clear.
In your test, you have also some problems. I think that they have no consequences but they tend to show that you are discovering Mockito.
You wrote:
#Mock
private NewsListPresenter mNewsListPresenter;
Annotates the field with #Mock, tells to mockito to create a mock of NewsListPresenter which the class under test. (Not a big problem since your creating the real instance in the setUp). You could have a look to #InjectMocks (even if i am not a big fan of it).
Another point is that you use both #RunWith(MockitoJUnitRunner.class) and
MockitoAnnotations.initMocks(this), you could avoid the last one since it is invoked by the runner. Furthermore, the runner is a better choice since it validate framework usage.
My last observation, is on your design. May be it is an android constraint, but you are injecting the context both in the presenter constructor and the getNewsFeed method, this seems weird and could leads to inconsistant situation. I would choose the constructor injection (more object oriented design).
Another way to ease testing could be to extract the major parts of the method in an utility class and test the different branches (null stream, empty stream, valid stream, IOException...) without the need of mocking a context and resources:
class IOUtils {
static String readFromStream(InputStream inputStream) {
....
}
}
Note that guava provides a similar utility.
Related
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.
I'm trying to carry out junit test for the Android-DDP library.
To initialize the meteor object, we need a reference to a android context which I'm able to achieve using Robolectric. But the web-sockets is probably talking to the server on a different thread because of which the callback methods are not called and the test methods are getting end.
I used netstat to check if the android client is trying to communicate or not. It shows various ping/pong messages. So, Yes it is trying to talk to the server.
I went through this tutorial as well,
Android AsyncTask testing with Android Test Framework. This one tells how to handle the network on UI thread. But nothing seems right.
The sample code, I have worked is:
#Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
#RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
private MainActivity activity;
private Meteor meteor;
private String globalUrl = "ws://10.0.3.222:3000/websocket";
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
meteor = new Meteor(activity, globalUrl);
meteor.reconnect();
/*
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
}
#Test
public void validateMeteorIsConnected() {
assertTrue(meteor.isConnected());
}
}
Any help would be appreciable. Thanks in advance.
You defined two methods, setup() and validateMeteorIsConnected(), but where are they called?
First, your setup is not correct. After your call to new Meteor(...), you don't need the reconnect() call because the constructor does already establish the connection.
Moreover, you must set up a listener so that you know when the connection has been established or data comes in. This is done with mMeteor.setCallback(...); where the parameter is this or activity.
As you said, the work is done on a different thread and everything is asynchronous.
So you can't just call validateMeteorIsConnected() immediately after connecting.
You need some timer, as shown in the question that you linked to.
Iam attempting to convert a resource image to a bitmap to test it in an Instrumentation test in android. originally i had this as a regular test extending testcase. my issue is with this
protected void setUp()
{
testbmap=BitmapFactory.decodeResource(getInstrumentation().
getContext().getResources(), R.drawable.ic_launcher);
}
then i test that the bitmap is not null but the test fails.
public void testnotnull()
{
assertNotNull(testbmap);
}
so iam doing something wrong here i think it could b something to do with the first param in the decoderecource(), maybe iam not pointing to the correct resources? I also tried getApplicationContext.getResources() method but iam not too sure on this one.
can anyone help me?
I solved this problem changing #getContext() which will return Context of instrumentation's package to #getTargetContext() which will return context of target application.
Return a Context for the target application being instrumented. Note
that this is often different than the Context of the instrumentation
code, since the instrumentation code often lives is a different
package than that of the application it is running against. See
getContext to retrieve a Context for the instrumentation code.
Kotlin method:
#Test
fun bitmapIsNotNull() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val bitmap = BitmapFactory.decodeResource(context.resources, R.raw.any_image)
Assert.assertNotNull(bitmap)
}
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);
My test class has 3APIs that run three test cases (test***()).
When I run the project as JUnit test case, how stop one or more of these 3 test cases to execute?
Basically, how to selectively run test cases present in the same file? {Putting them in a separate class is not really the solution!! :)}
Rc
If you are using Eclipse and just want to run a single test method instead of the whole suite or the whole test class, you just right click the method-name and choose "Run as.." -> "Android JUnit Test"
To selective skip test cases with JUnit you can add an #Ignore annotation above the test method(s) you don't want to run.
You can also do this from the commandline:
adb shell am instrument -e class com.android.demo.app.tests.FunctionTests#testCamera com.android.demo.app.tests/android.test.InstrumentationTestRunner
In this example, you're running only test method 'testCamera' in class FunctionTests. You can add multiple values via the -e argument.
Since there is no #Ignore annotation in JUnit 3, I had to figure out a workaround to ignore the long-running activity/instrumentation test cases from my test suite to be able to run the unit tests:
public class FastTestSuite extends TestSuite {
public static Test suite() {
// get the list of all the tests using the default testSuiteBuilder
TestSuiteBuilder b = new TestSuiteBuilder(FastTestSuite.class);
b.includePackages("com.your.package.name");
TestSuite allTest = b.build();
// select the tests that are NOT subclassing InstrumentationTestCase
TestSuite selectedTests = new TestSuite();
for (Test test : Collections.list(allTest.tests())) {
if (test instanceof TestSuite) {
TestSuite suite = (TestSuite) test;
String classname = suite.getName();
try {
Class<?> clazz = Class.forName(classname);
if (!InstrumentationTestCase.class.isAssignableFrom(clazz)) {
selectedTests.addTest(test);
}
} catch (Exception e) {
continue;
}
}
}
return selectedTests;
}
}
Simply run this test suite as an Android JUnit test.