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

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 {
}

Related

How to correctly implement and test Custom Lint Rules in Android Studio?

I'm following this tutorial and this Custom Detector Example in order to implement Custom Lint Rules. Basically what I've done is:
Create a new Android Project in Android Studio;
Create a java module for project created in step 1;
On module's build.gradle, import Lint API dependencies;
Create an Issue & IssueRegistry & CustomDetector;
Reference the IssueRegistry on module's build.gradle;
Create Unit tests;
My problem is, during the execution of my JUnits, I always receive "No Warning". When I debug the test, I can see that my Custom Detector isn't called, what am I doing wrong?
Strings.java
public class Strings {
public static final String STR_ISSUE_001_ID = "VarsMustHaveMoreThanOneCharacter";
public static final String STR_ISSUE_001_DESCRIPTION = "Avoid naming variables with only one character";
public static final String STR_ISSUE_001_EXPLANATION = "Variables named with only one character do not pass any meaning to the reader. " +
"Variables name should clear indicate the meaning of the value it is holding";
}
Issues.java
public class Issues {
public static final
Issue ISSUE_001 = Issue.create(
STR_ISSUE_001_ID,
STR_ISSUE_001_DESCRIPTION,
STR_ISSUE_001_EXPLANATION,
SECURITY,
// Priority ranging from 0 to 10 in severeness
6,
WARNING,
new Implementation(VariableNameDetector.class, ALL_RESOURCES_SCOPE)
);
}
IssuesRegistry.java
public class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
#Override
public List<Issue> getIssues() {
List<Issue> issues = new ArrayList<>();
issues.add(ISSUE_001);
return issues;
}
}
VariableNameDetector.java
public class VariableNameDetector extends Detector implements Detector.JavaScanner {
public VariableNameDetector() {
}
#Override
public boolean appliesToResourceRefs() {
return false;
}
#Override
public boolean appliesTo(Context context, File file) {
return true;
}
#Override
#Nullable
public AstVisitor createJavaVisitor(JavaContext context) {
return new NamingConventionVisitor(context);
}
#Override
public List<String> getApplicableMethodNames() {
return null;
}
#Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
List<Class<? extends Node>> types = new ArrayList<>(1);
types.add(lombok.ast.VariableDeclaration.class);
return types;
}
#Override
public void visitMethod(
JavaContext context,
AstVisitor visitor,
MethodInvocation methodInvocation
) {
}
#Override
public void visitResourceReference(
JavaContext context,
AstVisitor visitor,
Node node,
String type,
String name,
boolean isFramework
) {
}
private class NamingConventionVisitor extends ForwardingAstVisitor {
private final JavaContext context;
NamingConventionVisitor(JavaContext context) {
this.context = context;
}
#Override
public boolean visitVariableDeclaration(VariableDeclaration node) {
StrictListAccessor<VariableDefinitionEntry, VariableDeclaration> varDefinitions =
node.getVariableDefinitionEntries();
for (VariableDefinitionEntry varDefinition : varDefinitions) {
String name = varDefinition.astName().astValue();
if (name.length() == 1) {
context.report(
ISSUE_001,
context.getLocation(node),
STR_ISSUE_001_DESCRIPTION
);
return true;
}
}
return false;
}
}
}
build.gradle
apply plugin: 'java'
configurations {
lintChecks
}
ext {
VERSION_LINT_API = '24.3.1'
VERSION_LINT_API_TESTS = '24.3.1'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.tools.lint:lint-api:$VERSION_LINT_API"
implementation "com.android.tools.lint:lint-checks:$VERSION_LINT_API"
testImplementation "com.android.tools.lint:lint-tests:$VERSION_LINT_API_TESTS"
}
jar {
manifest {
attributes('Lint-Registry': 'br.com.edsilfer.lint_rules.resources.IssueRegistry')
}
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
TestVariableNameDetector.java
private static final String ARG_DEFAULT_LINT_SUCCESS_LOG = "No warnings.";
#Override
protected Detector getDetector() {
return new VariableNameDetector();
}
#Override
protected List<Issue> getIssues() {
return Collections.singletonList(Issues.ISSUE_001);
}
public void test_file_with_no_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test.java", "public class Test {public String sampleVariable;}"))
);
}
public void test_file_with_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test3.java", "public class Test {public String a;bnvhgvhj}"))
);
}
}
P.S.: on Java's module I do not have access to assetsor res folder, that is the reason why I've created a String.java and I'm using java(to, source) in my Unit test - I assumed that this java method does the same as the xml from the tutorial link I referenced at the top of this question.
It turned out that in my case the problem was with the JUnit itself. I think that the way I was attempting to simulate the file was wrong. The text below is part of the README.md of a sample project that I've created in order to document what I've learned from this API and answers the question in the title:
Create
Create a new Android Project;
Create a new Java Library Module - Custom Lint Rules are packaged into .jar libraries once they are ready, therefore the easiest way to implement them using them is inside a Java Module Library;
On module's build.gradle:
add target and source compatibility to Java 1.7;
add dependencies for lint-api, lint-checks and test dependencies;
add jar packing task containing two attributes: Manifest-Version and Lint-Registry, set the first to 1.0 and the second as the full path to a class that will later on contain the issue's catalog;
add a default tasl assemble;
[OPTIONAL]: add a task that will copy the generated .jar into ~/.android/lint;
Check REF001 and choose a Detector that best suits your needs, based on it create and implement a class to fulfill the Detector's role;
Still based on REF0001 chosen file, create and implement a Checker class, later referring to it inside Detector's createJavaVisitor() method;
for the sake of SRP, do not place Checker in the same file of Detector's class;
Copy the generated .jar file from build/lib to ~/.android/lint - if you added a task on build.gradle that does this you can skip this step;
Restart the computer - once created and moved into ~/.android/lint, the Custom Rules should be read by Lint next time the program starts. In order to have the alert boxes inside Android Studio, it is enough to invalidate caches and restart the IDE, however, to have your custom rules caught on Lint Report when ./gradlew check, it might be necessary to restart your computer;
Testing Detectors and Checkers
Testing Custom Rules is not an easy task to do - mainly due the lack of documentation for official APIs. This section will present two approaches for dealing with this. The main goal of this project is to create custom rules that will be run against real files, therefore, test files will be necessary for testing them. They can be places in src/test/resources folder from your Lint Java Library Module;
Approach 01: LintDetectorTest
Make sure you've added all test dependencies - checkout sample project's build.gradle;
Copy EnhancedLintDetectorTest.java and FileUtils.java into your project's test directory;
There is a known bug with Android Studio that prevents it from seeing files from src/test/resources folder, these files are a workaround for that;
EnhancedLintDetectorTest.java should return all issues that will be subject of tests. A nice way to do so is getting them from Issue Registry;
Create a test class that extends from EnhancedLintDetectorTest.java;
Implement getDetector() method returning an instance of the Detector to be tested;
Use lintFiles("test file path taking resources dir as root") to perform the check of the Custom Rules and use its result object to assert the tests;
Note that LintDetectorTest.java derives from TestCase.java, therefore, you're limited to JUnit 3.
Approach 02: Lint JUnit Rule
You might have noticed that Approach 01 might be a little overcomplicated, despite the fact that you're limited to JUnit 3 features. Because of that GitHub user a11n created a Lint JUnit Rule that allows the test of Custom Lint Rules in a easier way that counts with JUnit 4 and up features. Please, refer to his project README.md for details about how to create tests using this apprach.
Currently, Lint JUnit Rule do not correct the root dir for test files and you might no be able to see the tests passing from the IDE - however it works when test are run from command line. An issue and PR were created in order to fix this bug.
I'm not sure how to use the AST Api, however I'm personally using Psi and this is one of my lint checks that are working for me.
public final class RxJava2MethodCheckReturnValueDetector extends Detector implements Detector.JavaPsiScanner {
static final Issue ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE =
Issue.create("MethodMissingCheckReturnValue", "Method is missing the #CheckReturnValue annotation",
"Methods returning RxJava Reactive Types should be annotated with the #CheckReturnValue annotation.",
MESSAGES, 8, WARNING,
new Implementation(RxJava2MethodCheckReturnValueDetector.class, EnumSet.of(JAVA_FILE, TEST_SOURCES)));
#Override public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
}
#Override public JavaElementVisitor createPsiVisitor(#NonNull final JavaContext context) {
return new CheckReturnValueVisitor(context);
}
static class CheckReturnValueVisitor extends JavaElementVisitor {
private final JavaContext context;
CheckReturnValueVisitor(final JavaContext context) {
this.context = context;
}
#Override public void visitMethod(final PsiMethod method) {
final PsiType returnType = method.getReturnType();
if (returnType != null && Utils.isRxJava2TypeThatRequiresCheckReturnValueAnnotation(returnType)) {
final PsiAnnotation[] annotations = method.getModifierList().getAnnotations();
for (final PsiAnnotation annotation : annotations) {
if ("io.reactivex.annotations.CheckReturnValue".equals(annotation.getQualifiedName())) {
return;
}
}
final boolean isMethodMissingCheckReturnValueSuppressed = context.getDriver().isSuppressed(context, ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, method);
if (!isMethodMissingCheckReturnValueSuppressed) {
context.report(ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, context.getLocation(method.getNameIdentifier()), "Method should have #CheckReturnValue annotation");
}
}
}
}
}
Checkout the many more I wrote here.

Android - Testing a string resource with Robolectric

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

Using Roboguice in Tests in Android Studio

So after spending a day or so trying to get robolectric to work with gradle using the android-gradle-plpugin I read that the creator thinks it's too much hassle and doesn't use it himself
So thats a big enough reason for me not to use it either. However now I cannot find any docs on how to set up normal android tests to run on the emulator. Its seems they all relate to eclipse. How do I get normal Android testings running with Android studio. I presume I need to modify build.gradle but how do I do that?
I would also like to use roboguice to inject my dependencies into the test cases.
EDIT
So I took a stab in the dark and I tried this but the test returns false (a fail)
public class SearchTest extends ActivityTestCase {
#Inject
private ObjectMapper objectMapper;
#Override
protected void setUp() throws Exception {
super.setUp();
RoboInjector injector = RoboGuice.getInjector(getActivity());
injector.injectMembersWithoutViews(this);
}
public void shouldSerialise() {
System.out.println("called should serialise");
Assert.assertNotNull(objectMapper);
}
}
EDIT 2
So I have tried a different approach. I followed this tutorial which does seem to run the test however I am having a problem with providing a manifest find as I get the following error,
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).
I then used this test runner instead...
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
public RobolectricGradleTestRunner(Class<?> testClass) throws org.junit.runners.model.InitializationError {
super(testClass);
}
#Override protected AndroidManifest getAppManifest(Config config) {
String manifestProperty = System.getProperty("android.manifest");
if (config.manifest().equals(Config.DEFAULT) && manifestProperty != null) {
String resProperty = System.getProperty("android.resources");
String assetsProperty = System.getProperty("android.assets");
return new AndroidManifest(Fs.fileFromPath(manifestProperty), Fs.fileFromPath(resProperty),
Fs.fileFromPath(assetsProperty));
}
return super.getAppManifest(config);
}
}
With no luck. Would I be better reverting back to intellij and purely using maven?

Dynamically loading a library at runtime from an Android application

I followed the exact steps given in the post below:
Is it possible to dynamically load a library at runtime from an Android application?
I am using and android 2.2 phone for testing and getting an error which is driving me crazy :(
07-27 01:24:55.692: W/System.err(14319): java.lang.ClassNotFoundException: com.shoaib.AndroidCCL.MyClass in loader dalvik.system.DexClassLoader#43abbc20
Can someone help me what to do now..i have tried various suggested solutions on different posts
I was wondering whether this was feasible so I wrote the following class:
package org.shoaib.androidccl;
import android.util.Log;
public class MyClass {
public MyClass() {
Log.d(MyClass.class.getName(), "MyClass: constructor called.");
}
public void doSomething() {
Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
}
}
And I packaged it in a DEX file that I saved on my device's SD card as /sdcard/testdex.jar.
Then I wrote the program below, after having removed MyClass from my Eclipse project and cleaned it:
public class Main extends Activity {
#SuppressWarnings("unchecked")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final String libPath = Environment.getExternalStorageDirectory() + "/testdex.jar";
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shoaib.androidccl.MyClass");
final Object myInstance = classToLoad.newInstance();
final Method doSomething = classToLoad.getMethod("doSomething");
doSomething.invoke(myInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I guess your problem lies on the way you package MyClass in a DEX. How do you do that?
I followed Shlublu's thread and tried myself and succeeded. I don't know how to "package MyClass in a DEX", so I simply build an android apk and put it in the sdcard. And the code worked.
Are you wraping MyClass in a classes.dex? DexClassLoader will only look for classes.dex in your jar/dex. Maybe you can first try what I did (build an signed apk instead of jar, but you don't need to install the apk), and see if it's working. Hope this helps!

Android Eclipse jUnit does not see superclass

This may be a simple answer.
1) Create a jar file with this code:
package com.myCompany.base;
public class Dex1 {
public String getTerm1() {
return "Term 1";
}
}
This is compiled to Dex1.jar using NetBeans.
2) Created a 'Hello world' android application in Eclipse. Add the code for Dex2 that extends Dex1. Copy and add Dex1.jar to the java build path.
package com.myCompany;
import com.myCompany.base.Dex1;
public class Dex2 extends Dex1 {
public String getTerm2() {
return getTerm1() + " Term 2";
}
}
in my onCreate() call:
editText.setText(dex2.getTerm2());
Everything works Great! I get the correct string displayed on the android screen. Yea!
Now the problem starts:
3) Create a jUnit 3 test case using Eclipse command File -> New -> Project -> Android Text Project command and add the code:
package com.myCompany.test;
import junit.framework.TestCase;
import com.myCompany.Dex2;
public class Dex2Test extends TestCase {
protected void setUp() throws Exception {
super.setUp();
dex2 = new Dex2();
}
protected void tearDown() throws Exception {
super.tearDown();
}
Dex2 dex2;
public void testGetTerm2() {
/*line 21 */ assertEquals("Term 1 Term 2", dex2.getTerm2());
}
public void testGetTerm1() {
/* line 25 */ assertEquals("Term 1", dex2.getTerm1());
}
}
On Line 25 the compiler gives a 'method undefined' error for getTerm1(). I don't understand why this is an error?
I tried to add the Dex1.jar to the java Build path of the test project, it compiles but we receive a run time error 'NoClassDefFoundError'. Yuch!
More Information 16Mar2012
I set this up using Plan Java classes, same jar file, to remove Android and it worked. This makes me conclude there must be some anomaly in Android/DalvikVM (aka DavrosVM).
More Information 16Mar2012 End
Am I missing something?
Is the jar file built incorrectly (didn't think that was possible)?
Am I importing the jar file incorrectly?
Is it just crazy to expect to import and override a class in a jar file?
Thank you for reading, please reply.
Fish
Take a look at Android Testing: External libraries which I guess have the solution to your problem.
What you may be doing wrong is not exporting your library in Order and Export under Java Build Path.

Categories

Resources