Android - Testing a string resource with Robolectric - android

I just want to test that getting a String resource equals what I think it should equal. My issue seems to be that I have Realm in my project. I know that Robolectric doesn't support Realm (it states it in the documentation), but I'm not invoking Realm at all, so I feel like there might be a way to do this.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21, manifest = "app/src/main/AndroidManifest.xml")
public class ResourceTester {
#Test
public void testingString() {
String resourceString = RuntimeEnvironment.application.getString(R.string.app_name);
assertEquals(resourceString, "Yeah");
}
}
It does look like it IS trying to invoke Realm
java.lang.UnsatisfiedLinkError: Can't load library:
/var/folders/3x/ddxtg5fs1hxgqrp6h63vsc140000gp/T/android-tmp-
robolectric3612430387389787158/app_lib/librealm-jni.dylib.3.5.0
EDIT: I tried some more things, and it seems like setting the manifestin the #Config annotation is the issue, but then I get a android.content.res.Resources$NotFoundException: unknown resource 2131362072
Any other thoughts? Can I make another Application class where Realm isn't called? How would the /test directory know that?
EDIT FOR DAVID:
I tried this:
#RunWith(RobolectricGradleTestRunner.class)
#Config(application = TestingApplication.class, constants = BuildConfig.class, sdk = 21)
public class ResourceTester {
#Test
public void newTestingTests() throws Exception {
String appName = RuntimeEnvironment.application.getString(R.string.app_name);
}
}
but I get a:
android.content.res.Resources$NotFoundException: unknown resource 2131362072
If I change it to
#RunWith(RobolectricTestRunner.class)
//#RunWith(RobolectricGradleTestRunner.class)
#Config(application = TestingApplication.class, constants = BuildConfig.class, sdk = 21)
public class ResourceTester {
#Test
public void newTestingTests() throws Exception {
String appName = RuntimeEnvironment.application.getString(R.string.app_name);
}
}
I get
WARNING: No manifest file found at ./AndroidManifest.xml.Falling back to the Android OS resources only.
To remove this warning, annotate your test class with #Config(manifest=Config.NONE).
android.content.res.Resources$NotFoundException: unknown resource 2131362072

If you have a heavyweight Application class (e.g., with dependencies on Realm, Crashlytics etc.) and your unit tests do not refer to these you can use android.app.Application as your Application class in the config:
#RunWith(RobolectricTestRunner.class)
#Config(application = android.app.Application.class, manifest="src/main/AndroidManifest.xml", sdk = 23)
public class ResourceTester {
Also make sure you have Working Directory set to $MODULE_DIR$ if you are using Mac or Linux as per the getting started instructions

Related

Robolectric: “AndroidManifest.xml not found” and "Unable to find resource ID #0x7f09001b"

I'm running some tests with Roboletric, but I came across a issue that I can't solve.
When I run the test, the following error appears with the "AndroidManifest":
WARNING: No manifest file found at .\AndroidManifest.xml.
Falling back to the Android OS resources only. To remove this warning, annotate
your test class with #Config(manifest=Config.NONE).
No such manifest file: .\AndroidManifest.xml
I've tried these solutions that failed:
#Config (manifest = Config.DEFAULT_MANIFEST_NAME)
#Config(manifest = Config.NONE, constants = BuildConfig.class, sdk = 26)
#Config( constants = BuildConfig.class, manifest="src/main/AndroidManifest.xml", sdk = 26 )
And the other error during execution is:
android.content.res.Resources$NotFoundException: Unable to find
resource ID #0x7f09001b in packages [android, org.robolectric.default]
...
at
com.example.robertoassad.alltestsmerge.MainActivity.onCreate(MainActivity.java:52)
This line that have the error is the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Specifically in: setContentView(R.layout.activity_main);
For me I didn't see sense in this issue ...
DETAILS:
The test class is on the folder: app\src\test\java\com\example\robertoassad
The test is:
#RunWith( RobolectricTestRunner.class)
public class Roboletric {
#Test
public void clickingLogin_shouldStartLoginActivity() {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
activity.findViewById(R.id.button2).performClick();
Intent expectedIntent = new Intent(activity, SecondActivity.class);
Intent actual = ShadowApplication.getInstance().getNextStartedActivity();
assertEquals(expectedIntent.getComponent(), actual.getComponent());
}
}
I had a similar problem to the one you face. The post by jongerrish on the Robolectric GitHub Issue about this resolved the problem for me.
The aspect of the answer that worked for me was to add a testOptions block in my module's build.gradle file:
testOptions {
unitTests {
includeAndroidResources = true
}
}
After adding this block my tests were able to run and access String resources.
This problem bug me for some time, and here is my note in my test code.
About manifest location
With Gradle build system, Robolectric looks for AndroidManifest.xml in the following order.
Java resource folder
build/intermediates/manifests/[full or fast-start]/[build-type]
So it is a common mistake to specify the location of AndroidManifest.xml according to source code folder organization (e.g. src/main/AndroidManifest.xml) . The specified AndroidManifest.xml location affect how Robolectric look for merged resources as well. So if some resource is not found in test, it is probably due to incorrect setting of AndroidManifest.xml location.
That said, the Android Gradle plugin merge the AndroidManifest.xml and put the result under the above mentioned intermediates directory. So the content of src/main/AndroidManifest.xml affect the test result.
So if you want to specify manifest option in #Config, just use #Config(manifest=“AndroidManifest.xml”) should probably be fine. If you want to use an alternate AndroidManifest.xml, put it in Java resources folder, and specify #Config according to the relative path in resources folder.
I was also facing same problem while testing my library module from app. Now my Receievers and Service are in my library, so to test those , i had to implement custom Test Class, so Roboelectric can point to my library manifest and not the app manifest :
import android.os.Build;
import org.junit.runners.model.InitializationError;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.res.Fs;
import java.io.File;
import java.io.IOException;
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
private static final String PROJECT_DIR =
"C:/MyProject/";
private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC =
Build.VERSION_CODES.JELLY_BEAN_MR2;
public RobolectricGradleTestRunner(final Class<?> testClass) throws Exception {
super(testClass);
}
private static AndroidManifest getAndroidManifest() {
String manifestPath = PROJECT_DIR+"/src/main/AndroidManifest.xml";
String resPath = PROJECT_DIR+"/src/main/res";
String assetPath = PROJECT_DIR+"/src/main/assets";
System.out.print("manifest path: "+manifestPath);
System.out.print("resPath path: "+resPath);
System.out.print("assetPath path: "+assetPath);
return new AndroidManifest(
Fs.fileFromPath(manifestPath), Fs.fileFromPath(resPath), Fs.fileFromPath(assetPath)) {
#Override public int getTargetSdkVersion() {
return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC;
}
};
}
private static String getProjectDirectory() {
String path = "";
try {
File file = new File("..");
path = file.getCanonicalPath();
path = path + "/app/";
} catch (IOException ex) {}
return path;
}
#Override public AndroidManifest getAppManifest(Config config) {
return getAndroidManifest();
}
}
and use it in your test class like :
#RunWith(RobolectricGradleTestRunner.class)
public class MyClassChangeTest {
}

Robolectric not calling Application.onCreate()

I am very much new to Robolectric. I am facing these issues after migrating from Robolectric v3.0 to v3.4.1.
Robolectric is not loading my AppController class which extends Application
I am getting class cast exception on casting to RuntimeEnvironment.application to my AppController, which shouldn't be the case as my AppController extends Application and was working fine before migrating to latest version.
Please find the code below for my test
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, manifest = "app/src/main/AndroidManifest.xml", sdk = 21)
public class FragmentTest {
private ExampleFragment fragment = SupportFragmentController.of(new ExampleFragment()).create().get();
private Context mContext;
#Before
public void setUp() throws Exception {
// RuntimeEnvironment.application.onCreate();
mContext =RuntimeEnvironment.application ;
}
#Test
public void testFragmentInstantiation() {
ExampleFragment.mAppController = (AppController)mContext;
}
To reproduce - run any test.
Robolectric version is 3.4.1.
The main advice is to do not put manifest file location in the #Config section unless you know what you're doing. So please change your test annotations to:
#Config(constants = BuildConfig.class, sdk = 21)
Also, I don't know if it is by purpose but you could use much newer android sdk in your test with the newest version of Robolectric

Cannot find android.app.ApplicationPackageManager

When compiling the following example from the robolectric migration guide
package com.jongla.soundmash.robolectric
import org.robolectric.shadows.ShadowApplicationPackageManager
import org.robolectric.annotation.Implements
import android.app.ApplicationPackageManager
#Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
class MyCustomPackageManager extends ShadowApplicationPackageManager {
}
AndroidStudio is giving me Unresolved reference: ApplicationPackageManager. Does anyone know what I need to do to get this example to compile? Do I need some additional testCompile package in gradle?
When I was looking through Roboloectric source code, I ve spotted attribute className to specify class name instead. And it works like magic.
#Implements(className = "android.app.ApplicationPackageManager", inheritImplementationMethods = true)
public class MyCustomPackageManager extends ShadowApplicationPackageManager {
}
Migration document is clearly incorrect, when suggesting to use value as ApplicationPackageManager class is private and not visible for user code.

Android - How to UnitTest a Logging class with mockito

I have written a class to manage logging within an android application project.
The LogManager is basically a wrapper for android.util.log
It handles logging to a file, if the application crashes, and standard debug logging.
I would like to unit test the class using JUnit.
I have tried the following but it does not seem to produce the results I would expect after reading the examples:
LogManager.class (This is a simplified version of the class I have used, for demonstration purposes)
public class LogManager implements ILogManager
{
public void log(String tag, String message)
{
Log.e(tag, message);
}
}
And here is my test class
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
#PrepareForTest({Log.class, LogManager.class})
public class LogManagerUnitTest
{
#Test
public void testLogConsoleInfo()
{
PowerMockito.mockStatic(Log.class);
LogManager.getInstance().log(LogLevel.INFO, "test", "test");
PowerMockito.verifyStatic(Mockito.times(1));
Log.e(anyString(), anyString());
}
}
My problem is that this passes no matter what I put.
E.g: if I instead replace the last call with Log.wtf(...) it still passes. I would have assumed that it should fail since Log.wtf was not called in the static class Log?
So my question is, why isn't this approach working as expected and what would be the correct way to do it?
I started a fresh project and was able to get it to fail tests and succeed appropriately using the following, so I'm assuming the runwith was the culprit:
#RunWith(PowerMockRunner.class)
#PrepareForTest(android.util.Log.class)
public class LoggerUnitTest {
#Test
public void testLog() throws Exception
{
PowerMockito.mockStatic(Log.class); // when(Log.e(anyString(), anyString())).thenReturn(1);
Logger.log("test", "test");
PowerMockito.verifyStatic(times(1));
Log.e(anyString(), anyString());
} }
For the RobolectricGradleTestRunner, the following incantation would have exposed your logging:
ShadowLog.stream = System.out
Robolectric does not print the Android system logging by default.
It's also worth noting that the RobolectricGradleTestRunner has been deprecated in favor of the fully operational RobolectricTestRunner (The above assignment is still effective)

How to create custom shadows in robolectric 3.0?

I need to mock some custom class (create for it a shadow).
I have already read on http://robolectric.org/custom-shadows/ how to do this.
so, i have some class:
public class MyClass {
public static int regularMethod() { return 1; }
}
I create a shadow:
#Implements(MyClass.class)
public class MyShadowClass {
#Implementation
public static int regularMethod() { return 2; }
}
And i set the shadow in Test-class:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, shadows={MyShadowClass.class})
public class MyTest {
#Test
public void testShadow() {
assertEquals(2, MyClass.regularMethod());
}
}
But the shadow is not used.
java.lang.AssertionError:
Expected :2
Actual :1
How to make any custom shadow visible for RobolectricGradleTestRunner?
I have already tried:
http://www.codinguser.com/2015/06/how-to-create-shadow-classes-in-robolectric-3/
https://github.com/jiahaoliuliu/RobolectricSample/blob/master/app-tests/src/main/java/com/jiahaoliuliu/robolectricsample/RobolectricGradleTestRunner.java
Mock native method with a Robolectric Custom shadow class
but i get various compilation errors, such as
InstrumentingClassLoaderConfig not found
Setup not found
how to use custom shadows correctly in robolectric 3.0?
Custom shadows should be avoided and must be a last ditch resort. It should only be used if you cannot do much refactor in your code which is preventing you from running your tests like a native method call. It's better to mock the object of that class or spy using powermock or mockito than custom shadow it. If it's a static method, then use powermock.
In our project, we had a class which had some native methods and it was the config class used everywhere in the app. So we moved the native methods to another class and shadowed that. Those native methods were failing the test cases.
Anyways here's how you can custom shadow in robolectric 3.0:
Create a custom test runner that extends RobolectricGradleTestRunner:
public class CustomRobolectricTestRunner extends RobolectricGradleTestRunner {
public CustomRobolectricTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
public InstrumentationConfiguration createClassLoaderConfig() {
InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
builder.addInstrumentedPackage("com.yourClassPackage");
return builder.build();
}
Make sure that that the package doesn't contain any test cases that you are running using robolectric.
I am Jiahao, the creator of the second repository that you are referring.
First of all thanks for to check my code. I do many researches on Android and I am glad that my research is useful for someone else.
Then, the Shadow about Robolectric. I am using Robolectric 3.1 in this project, to test how Robolectric 3 works with MarshMallow:
https://github.com/jiahaoliuliu/robolectricForMarshmallow
I have been testing the new Runtime Permission Manager, as well as shadowing application and activities.
Here is sample code of the shadowed activity:
import android.content.Context;
import com.jiahaoliuliu.robolectricformarshmallow.controller.MainController;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/**
* Created by Jiahao on 7/18/16.
*/
#Implements(MainController.class)
public class MainControllerShadow {
public void __constructor__ (Context context) {
// Not do anything
}
#Implementation
public String getTextToDisplay(boolean permissionGranted) {
return "Test";
}
}
https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/shadow/MainControllerShadow.java
And this is how I am using it in the unit test:
package com.jiahaoliuliu.robolectricformarshmallow;
import com.jiahaoliuliu.robolectricformarshmallow.shadow.MainControllerShadow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
/**
* Created by Jiahao on 6/30/16.
*/
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, manifest = Config.NONE, application = FoolApplication.class,
shadows = { MainControllerShadow.class}, sdk = 18)
public class MainActivityTest {
private MainActivity mMainActivity;
#Before
public void setUp() throws Exception {
mMainActivity = Robolectric.setupActivity(MainActivity.class);
}
#After
public void tearDown() throws Exception {
}
#Test
public void testOnCreate() throws Exception {
// Simple test to know that it works
assertTrue(true);
}
}
https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/MainActivityTest.java
As you can see, I am not using customized Gradle Test Runner. I have checked the source code of Robolectric, for version 3.0 and 3.1 (latest) it is good enough to just specify the shadow classes in the header.
I hope it helps

Categories

Resources