how to unit test an android module - android

I have an android library module that I would like to add unit tests to.
do I need to have the module in a project to be able to run the tests?
is there a way to test the module independently from a project?

To use JUnit tests for your Android application, you need to add it as dependency to your Gradle build file.
dependencies {
// Unit testing dependencies
testCompile 'junit:junit:4.12'
// Set this dependency if you want to use the Hamcrest matcher library
testCompile 'org.hamcrest:hamcrest-library:1.3'
// more stuff, e.g., Mockito
}
You can also instruct the Gradle build system to return default values for method calls in the android.jarwith the following configuration in your Gradle build file.
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
In your app/src/test directory create the following two test methods for the ConverterUtil class.
package com.vogella.android.temperature.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.vogella.android.temperature.ConverterUtil;
public class ConverterUtilTest {
#Test
public void testConvertFahrenheitToCelsius() {
float actual = ConverterUtil.convertCelsiusToFahrenheit(100);
// expected value is 212
float expected = 212;
// use this method because float is not precise
assertEquals("Conversion from celsius to fahrenheit failed", expected, actual,
0.001);
}
#Test
public void testConvertCelsiusToFahrenheit() {
float actual = ConverterUtil.convertFahrenheitToCelsius(212);
// expected value is 100
float expected = 100;
// use this method because float is not precise
assertEquals("Conversion from celsius to fahrenheit failed", expected, actual,
0.001);
}
}
Ensure your unit tests are correctly implemented by running test tests. They should run successfully.refer this link

Related

Cannot resolve symbol DaggerAppComponent

There is my GitHub project - https://github.com/alekseytimoshchenko/MVVM_FLOW that I haven't used a lot of time and today I decided to check it out as a result I faced some compile/Gradle/build issues, so I needed to update some dependency and other things. Eventually looks like everything is ok, however, I got a compile
error: cannot find symbol
import com.example.krokosha.quizyourself.di.components.DaggerAppComponent;
This likely happens due to some specific dependencies or their version, I went through a few answers here on SO, but looks like everything is correct, but still, this class is not generated.
If it is suitable you are welcome to take a look at the project by the ref provided above, otherwise, I'll put some code here
There are my grale dependencies
...
//Dependency Injection
api "com.google.dagger:dagger:2.35.1"
annotationProcessor "com.google.dagger:dagger-compiler:2.28.3"
api "com.google.dagger:dagger-android:2.35.1"
api "com.google.dagger:dagger-android-support:2.28.3" // if you use the support libraries
annotationProcessor "com.google.dagger:dagger-android-processor:2.28.3"
...
and there is my class where I got an error:
package com.example.krokosha.quizyourself.di;
import android.content.Context;
import com.example.krokosha.quizyourself.di.components.AppComponent;
import com.example.krokosha.quizyourself.di.components.BaseComponent;
import com.example.krokosha.quizyourself.di.components.BaseComponentBuilder;
import com.example.krokosha.quizyourself.di.components.DaggerAppComponent;
import com.example.krokosha.quizyourself.di.moduls.AppModule;
import com.example.krokosha.quizyourself.di.moduls.BaseModule;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
public class ComponentsHolder
{
private final Context context;
#Inject
Map<Class<?>, Provider<BaseComponentBuilder>> builders;
private Map<Class<?>, BaseComponent> components;
private AppComponent appComponent;
public ComponentsHolder(Context context)
{
this.context = context;
}
public ComponentsHolder init()
{
appComponent = DaggerAppComponent.builder().appModule(new AppModule(context)).build();
appComponent.injectComponentsHolder(this);
components = new HashMap<>();
return this;
}
public BaseComponent getBaseComponent(Class<?> cls)
{
return getBaseComponent(cls, null);
}
public BaseComponent getBaseComponent(Class<?> cls, BaseModule module)
{
BaseComponent component = components.get(cls);
if (component == null)
{
BaseComponentBuilder builder = builders.get(cls).get();
if (module != null)
{
builder.module(module);
}
component = builder.build();
components.put(cls, component);
}
return component;
}
public void releaseBaseComponent(Class<?> cls)
{
components.put(cls, null);
}
}
What is a possible problem here?
DaggerAppComponent is the file Dagger generates as an implementation of your AppComponent interface. If DaggerAppComponent is not being generated, it usually means that Dagger has emitted an error that you can read further up in your compiler log. If you see one of those errors, please edit it into your question.
Also, note that you're mismatching your Dagger versions: your libraries are 2.35.1 but your annotation processor is 2.28.3. In some cases this mismatch can cause a compilation failure, though it's more typical for it to cause the generated DaggerAppComponent file to fail to compile within the generated code. (The generated code calls the dagger libraries at runtime, so if the versions don't match the generated code might call into a class or method that doesn't exist.) Because DaggerAppComponent is missing entirely, it's less likely that the version skew is the root cause of your error here, but in any case you should always have all your Dagger versions consistent: api/implementation/annotationProcessor and between dagger and dagger.android.

Class.getResourceAsStream in Robolectric environment fails to return correct stream in M platform

I've got a problem with switching to M platform in robolectric runner.
Class.getResourceAsStream seems to perform differently when using Build.VERSION_CODES.M and Build.VERSION_CODES.LOLLIPOP_MR1
My application uses PhoneUtils library that loads its metadata using the following code:
static final MetadataLoader DEFAULT_METADATA_LOADER = new MetadataLoader() {
#Override
public InputStream loadMetadata(String metadataFileName) {
return MetadataManager.class.getResourceAsStream(metadataFileName);
}
};
When configuring test with:
#Config(
constants = BuildConfig::class,
sdk = intArrayOf(Build.VERSION_CODES.M)
)
resulting JarUrlConnection is set to:
sun.net.www.protocol.jar.JarURLConnection:jar:file:/C:/Users/motorro/.m2/repository/org/robolectric/android-all/6.0.0_r1-robolectric-0/android-all-6.0.0_r1-robolectric-0.jar!/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_US
(refers roboelectric platform mock) and fails to read a file.
If configured with:
#Config(
constants = BuildConfig::class,
sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP_MR1)
)
resulting JarUrlConnection is set to:
sun.net.www.protocol.jar.JarURLConnection:jar:file:/C:/Users/motorro/.gradle/caches/modules-2/files-2.1/com.googlecode.libphonenumber/libphonenumber/8.0.0/ce021971974ee6a26572e43eaba7edf184c3c63d/libphonenumber-8.0.0.jar!/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_US
which points to correct library file (test passes).
Here is the test:
#RunWith(RobolectricTestRunner::class)
class ExampleUnitTest {
companion object {
private val PHONE: Phonenumber.PhoneNumber
init {
PHONE = Phonenumber.PhoneNumber()
PHONE.countryCode = 7
PHONE.nationalNumber = 4956360636
}
}
#Test
#Config(
constants = BuildConfig::class,
sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP_MR1)
)
fun withLollipop() {
PhoneNumberUtil.getInstance().format(PHONE, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
}
#Test
#Config(
constants = BuildConfig::class,
sdk = intArrayOf(Build.VERSION_CODES.M)
)
fun withM() {
PhoneNumberUtil.getInstance().format(PHONE, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
}
}
Is it a roboelectric problem or do I miss some configuration here?
The duct-tape solution for now is to #Config all failing tests individually.
Current dependencies:
compile 'com.googlecode.libphonenumber:libphonenumber:8.0.0'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.1.4"
Here is a whole test project for your convenience.
GitHub issue
Issue fixed in Robolectric 3.2.2

Need help to write a unit test using Mockito and JUnit4

Need help to write a unit test for the below code using Mockito and JUnit4,
public class MyFragmentPresenterImpl {
public Boolean isValid(String value) {
return !(TextUtils.isEmpty(value));
}
}
I tried below method:
MyFragmentPresenter mMyFragmentPresenter
#Before
public void setup(){
mMyFragmentPresenter=new MyFragmentPresenterImpl();
}
#Test
public void testEmptyValue() throws Exception {
String value=null;
assertFalse(mMyFragmentPresenter.isValid(value));
}
but it returns following exception,
java.lang.RuntimeException: Method isEmpty in android.text.TextUtils
not mocked. See http://g.co/androidstudio/not-mocked for details. at
android.text.TextUtils.isEmpty(TextUtils.java) at ....
Because of JUnit TestCase class cannot use Android related APIs, we have to Mock it.
Use PowerMockito to Mock the static class.
Add two lines above your test case class,
#RunWith(PowerMockRunner.class)
#PrepareForTest(TextUtils.class)
public class YourTest
{
}
And the setup code
#Before
public void setup() {
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return !(a != null && a.length() > 0);
}
});
}
That implement TextUtils.isEmpty() with our own logic.
Also, add dependencies in app.gradle files.
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
Thanks Behelit's and Exception's answer.
Use PowerMockito
Add this above your class name, and include any other CUT class names (classes under test)
#RunWith(PowerMockRunner.class)
#PrepareForTest({TextUtils.class})
public class ContactUtilsTest
{
Add this to your #Before
#Before
public void setup(){
PowerMockito.mockStatic(TextUtils.class);
mMyFragmentPresenter=new MyFragmentPresenterImpl();
}
This will make PowerMockito return default values for methods within TextUtils
You would also have to add the relevant gradle depedencies
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
This is a known issue as mentioned by #Exception. In my case, I also stumbled upon same situation but on advice of senior dev decided to use Strings.isNullOrEmpty() instead of TextUtils.isEmpty(). It turned out to be a nice way to avoid it.
Update: I should better mention it that this utility function Strings.isNullOrEmpty() requires Guava library.
This is a known issue, due to a clause in Testing Fundamental of Android which says:
You can use the JUnit TestCase class to do unit testing on a class
that does not call Android APIs.
The default behavior is problematic when using classes like Log or TextUtils.
To sum up:
android.jar is mock before, so some Android API return value may not be as expected.
JUnit itself is a single measure for the java code, so try not to use the Android API methods.
Source: http://www.liangfeizc.com/2016/01/28/unit-test-on-android/
add this line in your gradle file in case of Android Studio.
android{
....
testOptions {
unitTests.returnDefaultValues = true
}
}
You should use Robolectric:
testImplementation "org.robolectric:robolectric:3.4.2"
And then
#RunWith(RobolectricTestRunner::class)
class TestClass {
...
}
Solution 1:
I would like to provide a Kotlin and a Java version.
Kotlin version:
import android.text.TextUtils
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Matchers.any
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
#RunWith(PowerMockRunner::class)
#PrepareForTest(TextUtils::class)
class UserOwnedDataTest1 {
#Before
fun setup() {
PowerMockito.mockStatic(TextUtils::class.java)
PowerMockito.`when`(TextUtils.isEmpty(any(CharSequence::class.java))).thenAnswer { invocation ->
val a = invocation.arguments[0] as? CharSequence
a?.isEmpty() ?: true
}
}
}
Java version:
import android.text.TextUtils;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.any;
#RunWith(PowerMockRunner.class)
#PrepareForTest(TextUtils.class)
public final class UserOwnedDataTest2 {
#Before
public void setup() {
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer((Answer<Boolean>) invocation -> {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return !(a != null && a.length() > 0);
});
}
}
Do not forget to add the dependencies:
testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
I remember that we still need another dependency, but not clearly.
Anyway you could fix the missing dependency easily.
Solution 2:
Or you could add the same package and class name with TextUtils
package android.text;
public class TextUtils {
public static boolean isEmpty( CharSequence str) {
return str == null || str.length() == 0;
}
}
I was able to solve this error by running the test class with the following.
#RunWith(RobolectricGradleTestRunner.class)
public class MySimpleTest {
.....a bunch of test cases
}
This wiki page explains in greater detail
https://github.com/yahoo/squidb/wiki/Unit-testing-with-model-objects
I replaces everywhere in my project TextUtils.isEmpty(...) with this:
/**
* Util class to be used instead of Android classes for Junit tests.
*/
public class Utils {
/**
* Returns true if the string is null or 0-length.
* #param str the string to be examined
* #return true if str is null or zero length
*/
public static boolean isEmpty(#Nullable CharSequence str) {
return str == null || str.length() == 0;
}
}
As a followup to Johnny's answer, to catch TextUtils.isEmpty(null) calls as well, you could use this piece of code.
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any()))
.thenAnswer((Answer<Boolean>) invocation -> {
Object s = invocation.getArguments()[0];
return s == null || s.length() == 0;
});
With newer power mock (2.0.9) and mockito (3.9.0) I had. to change execution to this one:
when(TextUtils.isEmpty(any())).thenAnswer((Answer<Boolean>) invocation -> {
CharSequence a = (CharSequence) invocation.getArguments()[0];
return a == null || a.length() == 0;
});

Custom gradle plugin with pmd, checkstyle and findbugs configured

Thanks for the advice, in the doc it says:
TextResource ruleSetConfig
Note: This property is incubating and may change in a future version of Gradle.
The custom rule set to be used (if any). Replaces ruleSetFiles, except that it does not currently support multiple rule sets. See the official documentation for how to author a rule set. Example: ruleSetConfig = resources.text.fromFile(resources.file("config/pmd/myRuleSets.xml"))
But if I try
setRuleSetConfig(project.resources.text.fromFile(project.resources.file("pmd.xml")))
it says:
Caused by: groovy.lang.MissingMethodException: No signature of method: org.gradle.api.internal.resources.DefaultResourceHandler.file() is applicable for argument types: (java.lang.String) values: [pmd.xml]
Possible solutions: find(), find(groovy.lang.Closure), use([Ljava.lang.Object;), is(java.lang.Object), with(groovy.lang.Closure), wait()
at com.barista_v.android_quality.MyPmdTask.<init>(MyPmdTask.groovy:20)
at com.barista_v.android_quality.MyPmdTask_Decorated.<init>(Unknown Source)
at org.gradle.api.internal.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:48)
at org.gradle.api.internal.ClassGeneratorBackedInstantiator.newInstance(ClassGeneratorBackedInstantiator.java:36)
at org.gradle.api.internal.project.taskfactory.TaskFactory$1.call(TaskFactory.java:121)
... 82 more
Also I am trying in this way::smile:
class MyPmdTask extends Pmd {
MyPmdTask() {
project.extensions.pmd.with {
reportsDir = project.file("$project.buildDir/outputs/")
}
source = project.fileTree('src/main/java').toList()
ruleSets = []
reports {
xml.enabled = false
html.enabled = true
}
setIgnoreFailures(true)
setRuleSetConfig(ResourceUtils.readTextResource(project, getClass().getClassLoader(), "pmd.xml"))
}
}
class ResourceUtils {
static TextResource readTextResource(Project project, ClassLoader classLoader, String fileName) {
println "reading config file $fileName"
def inputStream = classLoader.getResourceAsStream(fileName)
project.resources.text.fromString inputStream.text
}
}
and it set the config file but it dont use my rules, dont crash but dont use my rules.
I tried setting in
project.extensions.pmd.with {
// HERE
}
when I try getRuleSetConfig() it returns null but with setRuleSetConfig() appears to be setted but not used.
I dont want to create another repo just for configurations like here https://github.com/MovingBlocks/TeraConfig.
Related to: https://discuss.gradle.org/t/how-can-i-read-setup-resources-from-plugin-jar/13274/4
what can I do?
I was able to create a custom plugin that adds a PMD task of the built-in type, using a custom ruleset packaged in the plugin's src/main/resources directory; I'm using kotlin, but you can probably get the idea:
class BuildPlugin: Plugin<Project> {
override fun apply(project: Project) {
val android = project.extensions.getByType(BaseExtension::class.java)
project.pluginManager.apply(PmdPlugin::class.java)
project.extensions.getByType(PmdExtension::class.java).toolVersion = "5.5.2"
project.tasks.register("pmd", Pmd::class.java) {
it.source(android.sourceSets.getByName("main").java.sourceFiles,
android.sourceSets.getByName("test").java.sourceFiles,
android.sourceSets.getByName("androidTest").java.sourceFiles)
it.ruleSetConfig = project.resources.text.fromUri(javaClass.classLoader.getResource("config/pmd.xml"))
}
}
}
It's still a work in progress (for instance, I'm unsure if there are implications to resolving the android extension directly in my apply), but it seems to use my ruleset as expected!

Android Simple Unit Test Succeeds when Clearly Wrong

Whenever I run a simple JUnit test on Android studio, the tests always succeed no matter what..
I already hit Build Variant and this is under Unit Tests.
public class ECUserTests extends InstrumentationTestCase{
public void test() throws Exception{
final int expected = 1;
final int reality = 5;
assertEquals(expected, reality);
}
I added
testOptions {
unitTests.returnDefaultValues = true
}
To my build.gradle file and now this is something that clearly isn't true is returning as passed.
For the new unit test support, you don't need to inherit from InstrumentationTestCase, but you do need to annotate your test method.
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ECUserTests {
#Test
public void test() {
final int expected = 1;
final int reality = 5;
assertEquals(expected, reality);
}
}
To run unit test, it's required to select "Unit test" in Test Artifact under Build Variants and Android Studio version must be >1.1

Categories

Resources