Android Simple Unit Test Succeeds when Clearly Wrong - android

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

Related

how to unit test an android module

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

Mockk Missing calls inside every { ... } block

I'm stuck trying to mock some stuff with mockk:
I have the following setup on gradle
root:
|-- App (just a sample app for the SDK)
|-- SDK (SDK we develop) << apply plugin: 'com.android.library'
|-- SDKimpl.kt
|-- Foo (wrapper around a .jar library) << apply plugin: 'com.android.library'
|-- Foo.kt
So I'm writing an androidTest for the SDK and trying to mock Foo.kt.
There's nothing unusual about Foo class, just direct class Foo(private val someParams) {
So using androidTestImplementation "io.mockk:mockk-android:1.8.13" the mock goes:
val mock: Foo = mockk()
// val mock: Foo = mockkClass(Foo::class) // also tried this
every { mock.getData() } returns listOf("1", "2", "3")
I'm always getting the following crash:
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:42)
Also tried just to gather information:
running inside JVM test folder. It gets mocked without issues, but I can't run my test as JVM
running androidTest inside Foo module. Got the same crash
using mockkClass(Foo::class). Got some crash
using annotation #MockK and MockKAnnotations.init(this). Got some crash.
added Log.d before every { line and inside getData() method and it seems the actual real method from the class is getting called during the mock setup. That seems super weird to me.
Any idea what's going wrong here?
edit:
as requested, full code. I'm current working on an isolated project to try to isolate the error, so Foo is just:
class Foo {
fun getData(): String {
Log.d(TAG, "invoked foo.getData()")
return "trolololo"
}
}
and then I have FooTest in androidTest:
class FooTest {
#Test
fun mock_foo() {
val foo = mockk<Foo>()
every { foo.getData() } returns "zero"
assertEquals("zero", foo.getData())
}
}
It seems to be a Mockk opened issue: https://github.com/mockk/mockk/issues/182
2 possible quick fixes (pick one):
Run the Instrumented Tests in an emulator >= Android-P
Set Foo class as open (and the method(s) you want to mock too)
Try to check the official guide and see what is missing.
In my case, I tried to mock an extension in Kotlin but missed the mockkStatic
fun Date.asMyTime() : DateTime = DateTime(this, DateTimeZone.getDefault())
mockkStatic("packageName.FileNameKt") // This is what I was missing
every {
DateTime().asMyTime()
} returns mock(DateTime::class.java)
In my case I forgot to spyk the class I was applying every {...} to. 😳
val presenter = spyk(MyPresenter())
every { view.myFun(any()) } returns Unit
In my case, I've missed
#Before
fun setUp() {
MockKAnnotations.init(this)
}
In my case I tried to mock using mock() function instead mockk() (double k)
Make sure the object is really a mock, not the real object.
For instance:
- Sdk sdk = Sdk()
+ Sdk sdk = mockk()
every { sdk.crypto } returns mockk()
My problem was that I used a java class without getters
public class KeyStorePasswordPair {
public KeyStore keyStore;
public String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
I needed to add getters for the variables to make mockking work:
public class KeyStorePasswordPair {
public KeyStore getKeyStore() {
return keyStore;
}
public String getKeyPassword() {
return keyPassword;
}
private KeyStore keyStore;
private String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
Mockk Missing calls inside every { ... } block
may also be thrown when you have an every block defined on an object that is not a mockk, for example:
You define stub behavior like this
every { foo.getData() } returns DATA
and then try to:
every { DATA.getVersion() } returns VERSION
Where MY_THING and VERSION objects are declared (and instantiated) in the test class.
The error message is not very informative and a bit misleading in that case.
try like this
`when`(mock.getData()).thenReturn(listOf("1", "2", "3"))

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

Unit test Android, getString from resource

I am trying to do an unit test for an android app and I need to get a string from res.string resources. The class that I want to test is a POJO class. I am doing the app in two languages, due to this, I need to get a string from resource. The problem is that I cannot get the context or the activity, is possible? I know that with Instrumentation test I can do it, but I need to test some functions (white box test) before to do the instrumentation test (black box test).
This is the function that I have to test:
public void setDiaByText(String textView) {
getll_diaSeleccionado().clear();
if (textView.contains(context.getResources().getString(R.string.sInicialLunes))) {
getll_diaSeleccionado().add(0);
getIsSelectedArray()[0] = true;
getI_idiaSeleccionado()[0] =1;
} else
{
getIsSelectedArray()[0] = false;
getI_idiaSeleccionado()[0] =0;
}
}
And this is the test:
#Test
public void setDiaByTextView() {
String texto = "L,M,X,J,V,S,D";
alertaPOJO.setDiaByText(texto);
assertEquals(alertaPOJO.getIsSelectedArray()[0], true);
assertEquals(alertaPOJO.getI_idiaSeleccionado()[0], 1);
}
It crash when try to do context.getResources().getString(R.string.sInicialLunes))
If I put 'Mon' instead of context.getResources().getString(R.string.sInicialLunes)) or 'L' it work perfectly so, is possible to get the context or the activity in order to access to resource folder?
I am testing with Mockito and the setUp function is:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = Mockito.mock(Alerta.class);
Mockito.when(mContext.getApplicationContext()).thenReturn(mContext);
alertaPOJO = new AlertaPOJO();
}
Thanks
If you are using Context only for obtaining String resource, I would go by mocking only getResources().getString() part like this (see JUnit4 notation):
#RunWith(MockitoJUnitRunner.class)
public class AlertaPOJOTest {
#Mock
Context mMockContext;
#Test
public void setDiaByTextView() {
String texto = "L,M,X,J,V,S,D";
when(mMockContext.getString(R.string.sInicialLunes))
.thenReturn(INITIAL_LUNES);
alertaPOJO.setDiaByText(texto);
assertEquals(alertaPOJO.getIsSelectedArray()[0], true);
assertEquals(alertaPOJO.getI_idiaSeleccionado()[0], 1);
}
}
There are many reasons to stay with JVM tests, most important one, they are running quicker.
Untested: would it work to use the below, and probably targetContext?
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
You don't have a real android Context while you are using JVM unit test. For your case, maybe you can try Android Instrumentation Test, typically it is implemented in the "androidTest" directory of your project.
If you use MockK it's the same.
#RunWith(AndroidJUnit4::class)
class YourClassUnitTest : TestCase() {
#MockK
private lateinit var resources: Resources
#Before
public override fun setUp() {
MockKAnnotations.init(this)
}
#Test
fun test() {
every {
resources.getQuantityString(R.plurals.age, YEARS, YEARS)
} returns AGE
every {
resources.getString(
R.string.surname,
SURNAME
)
} returns TITLE
// Assume you test this method that returns data class
// (fields are calculated with getQuantityString and getString)
val data = getData(YEARS, SURNAME)
assertEquals(AGE, data.age)
assertEquals(TITLE, data.title)
}
companion object {
const val YEARS = 10
const val AGE = "$YEARS years"
const val SURNAME = "Johns"
const val TITLE = "Mr. $SURNAME"
}
}
See also Skip a parameter in MockK unit test, Kotlin to get a result of string resources for any data.

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;
});

Categories

Resources