I am currently starting to unit test my android application. I am having problems when the unit test exercise code that has log statements in it. Here is a specific case. I have a class called ServiceManager that has a setSystemPause() and a getSystemPause() method. I just want a simple unit test that exercise that logic
ServiceManager class:
public class ServiceManager implements IServiceManager {
private final static String TAG = "ServiceManager";
private boolean mSystemPauseStatus = false;
public boolean getSystemPause () {
Log.i ("TAG", "getSystemPause: " + mSystemPauseStatus);
return mSystemPauseStatus;
}
public void setSystemPause (boolean pauseStatus){
Log.i ("TAG", "setSystemPause: " + pauseStatus);
mSystemPauseStatus = pauseStatus;
}
}
The unit test:
public class ServiceManagerTest {
#Test
public void testSystemPause() throws Exception {
ServiceManager serviceManager = new ServiceManager();
serviceManager.setSystemPause(false);
assert (! serviceManager.getSystemPause());
serviceManager.setSystemPause(true);
assert (serviceManager.getSystemPause());
}
}
The problem are the "Log.i" statements in my code. That causes the following error:
java.lang.RuntimeException: Method i in android.util.Log not mocked.
I understand what is happening, during unit test the android.jar library that is used does not contain the real code and I need to mock that call to "Log.i".
But the code base that I am going to test contains a lot of Log statements. I don't want to mock each usage of the Log facility.
My question is how do people do unit testing in Android while having Log statements in their code. Is there another log facility that I can use in my code instead of the Log class.
I also read the page here:
https://developer.android.com/training/testing/unit-testing/local-unit-tests.html
They suggest doing this in my build.gradle file:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}
}
I don't want to resort to that because I just want the Log to appear. I want to properly mock all other facilities I will use in Android.
But will the Log statement affect the outcome of your unit tests? Problem is that Log is an Android-specific class, and can't be used as part of a JUnit 4 test as it's not part of the Java JDK. If you need Log statements to work as intended, either mock the behaviour out with Mockito, use returnDefaultValues = true, or run the test as a Connected Android Test (/androidTest folder instead of /test).
I personally use returnDefaultValues = true as you mention as Logging is something I'm not usually interested in when Unit Testing, only when I'm trying to track down specific bugs.
You could create a package level method in ServiceManager class which calls Log.i method.
public class ServiceManager implements IServiceManager {
private final static String TAG = "ServiceManager";
private boolean mSystemPauseStatus = false;
public boolean getSystemPause () {
log("TAG", "getSystemPause: " + pauseStmSystemPauseStatusatus);
return mSystemPauseStatus;
}
public void setSystemPause (boolean pauseStatus){
log("TAG", "setSystemPause: " + pauseStatus);
mSystemPauseStatus = pauseStatus;
}
void log(String tag, String message) {
Log.i (tag, message);
}
Then you can override this method in ServiceManagerTest to provide no implementation.
public class ServiceManagerTest {
#Test
public void testSystemPause() throws Exception {
ServiceManager serviceManager = createServiceManager();
serviceManager.setSystemPause(false);
assert (! serviceManager.getSystemPause());
serviceManager.setSystemPause(true);
assert (serviceManager.getSystemPause());
}
private ServiceManager createServiceManager() {
return new ServiceManager() {
#Override
void log(String tag, String message) {
//Do nothing or you could test that this method was called.
}
}
}
}
Related
For my espresso tests, I am searching for a way to let all tests fail before they run, if a specific condition is not met. How can I achieve this?
How about creating a jUnit rule to handle that? Specifically the Verifier rule. https://github.com/junit-team/junit4/wiki/rules#verifier-rule
Verifier is a base class for Rules like ErrorCollector, which can turn
otherwise passing test methods into failing tests if a verification check is failed.
private static String sequence;
public static class UsesVerifier {
#Rule
public final Verifier collector = new Verifier() {
#Override
protected void verify() {
sequence += "verify ";
}
};
#Test
public void example() {
sequence += "test ";
}
#Test
public void verifierRunsAfterTest() {
sequence = "";
assertThat(testResult(UsesVerifier.class), isSuccessful());
assertEquals("test verify ", sequence);
}
}
Have you tried System.exit(-1) or System.exit(0) ? One of these should serve your purpose depending on what condition you are trying to check. You can read more about them here.
I've started learning android unit tests, but it looks very hard to find some good guides or information. Every example have a stupid example about 2+2 = 4
Say I write a little SDK which has few functions
MySdk.Init(Context context)
MySdk.CallTask()
I create an androidTest file
How should I call my SDK functions to check how they work? Somewhere required parameters like int/string/context. I just really don't understand, please help me.
This is what I've tried
public class AndroidTest {
private Activity context;
//default test
#Test
public void addition_correct() throws Exception {
assertEquals(4, 2 + 2);
}
#Test
public void checkContext() {
context = getActivity();
assertNotNull(context);
}
#Test
public void testInitPhase() {
MySdk.Init(context, new SdkInitializationListener() {
#Override
public void onInitializationSuccessful(String adv_id) {
assert (adv_id != null);
}
#Override
public void onInitializationError() {
}
});
}
}
For context i was tried context = new mockContext();. It's passed as context = null and my SDK failed with initialization.
Unit tests are mainly about testing an individual class in isolation, so that you can check if individual public methods of a class behave as you intend them to, and continue to do so if you change that class' code in the future. Let's say you have a class like this:
public class UtilityFunctions {
public int double(int value) {
return value * 2;
}
public String mirror(String value) {
if (value == null) return "";
return value + new StringBuilder(value).reverse().toString();
}
}
You want to test these two methods with:
valid input values, and check the output is as expected
invalid values, and check that errors are handled accordingly (and the correct exceptions thrown if necessary)
So a test class for the above class may look like this
#RunWith(JUnit4.class)
public class UtilityFunctionsTest {
private UtilityFunctions utility;
#Before
public void setUp() {
// Initialises any conditions before each test
utility = new UtilityFunctions();
}
#Test
public void testDoubleFunction() {
assertEquals(2, utility.double(1));
assertEquals(8, utility.double(4));
assertEquals(-12, utility.double(-6));
assertEquals(0, utility.double(0));
}
#Test
public void testMirror() {
assertEquals("", utility.mirror(null));
assertEquals("", utility.mirror(""));
assertEquals("aa", utility.mirror("a"));
assertEquals("MirrorrorriM", utility.mirror("Mirror"));
}
}
These standard Java unit tests are run from the test directory. However, you'll need to run tests in the androidTest directory whenever you're using Android-specific classes such as Context. If you're creating a MockContext, you're simply creating an empty Context whose methods don't do anything.
Without me knowing anything about what your MySDK does, I think you may need to pass a fully-functioning Context into your class for your tests. The Android JUnit runner does provide this with InstrumentationRegistry.getTargetContext(), so for your example, you may need to add this #Before method:
#Before
public void setUp() {
context = InstrumentationRegistry.getTargetContext();
}
You'll also need to remove the context = getActivity(); line from your first test.
How to verify a void method call in Robolectric test case where as no data coming out the called method.
What to assert in this case? Below given an example of the requirement.
public class SampleClass(){
final String TAG = SampleClass.class.getSimpleName();
public void log(){
Log.d(TAG, "Entry Loggd");
}
}
#Test
public void logEntry_test(){
SampleClass sc = new SampleClass();
sc.log();
// What to assert here to verify this log method
}
First off, good on you for writing tests!!! There are a few ways to go about testing that an internal logger is called. It's equally as important to understand what you're looking to test. Testing that a class is logging a specific message is most likely a fragile test, so be fore-warned that you probably don't need it.
Method #1: Using Robolectric
Robolectic documentation doesn't lend itself to answering basic questions, but its codebase is very well documented with its tests. A basic understanding of its principles and how shadows work can get you a long way. ShadowLog tests lay the ground work to this solution.
#RunWith(RobolectricTestRunner.class)
public class SampleClassTest {
#Test
public void log_writesExpectedMessage() {
new SampleClass().log();
ShadowLog.LogItem lastLog = ShadowLog.getLogs().get(0);
assertThat(lastLog.msg).isEqualTo("some message");
// or
assertThat(lastLog.msg).isNotNull();
}
}
Tests using Robolectric v3.1.2
Add the following to your build.gradle file:
testCompile 'org.robolectric:robolectric:3.1.2'
Method #2: Making use of Abstractions
If your sample class derives from an Android class (Activity, Fragment, Application, etc), then using android.util.Log makes sense, but bear in mind that your test will need to be a Robolectric or AndroidInstrumented test. If your SampleClass is just some POJO, then using a simple logging framework may make your testing efforts easier. For example, using Jake Wharton's Timber, your class and test can be written as follows:
import timber.log.Timber;
public class SampleClass {
void log() {
Timber.d("some message");
}
}
// SampleClassTest.java
public class SampleClassTest {
// setting up a Tree instance that we define below
TestTree testTree = new TestTree();
#Test
public void log_writesExpectedMessage() {
// setting up Timber to us the test classes log writer
Timber.plant(testTree);
// invoke the logging function
new SampleClass().log();
// assert
assertThat(testTree.lastMessage).isEqualTo("some message");
}
private class TestTree extends Timber.Tree {
private String lastMessage;
#Override
protected void log(int priority, String tag, String message, Throwable t) {
lastMessage = message;
}
}
}
Good luck, happy testing!
In my understanding you want to mock static methods. I guess, using static mocks are not the most elegant way to testing. Better to use an abstraction as recommended by abest. Although, it can be done with PowerMock.
I'm new to Android unit test and was wondering how I can mock the context if I want to unit test the getSomething() below.
Thanks a lot in advance!
public class Provider {
private final String packageName;
public Provider(Context context) {
packageName = context.getPackageName();
}
public Data getSomething() {
return get(packageName);
}
private Data get(String packageName) {
// return something here based on the packageName
}
}
I tried
#Before
public void setUp() throws Exception {
provider = new Provider(mock(Context.class));
}
#Test
public void DoSomethingTest() {
final Data data = provider.getSomething();
assertThat(data).isNotNull();
}
But I got the error below:
java.lang.RuntimeException: Stub!
at android.content.Context.(Context.java:4)
at android.content.ContextWrapper.(ContextWrapper.java:5)
You call getPackageName(); on the Context-mock. To get this running you have to mock the method like:
Mockito.when(mock.getPackageName()).thenReturn("myPackage");
But this makes your test pretty much useless. But thinking about this, this isn't a test which I would write because (assuming it works as you expecting it) it just tests the framework method getPackageName(). In your tests you should test YOUR code or to be more specific your algorithms and not the successful call of methods.
I'm looking to do a simple test. I just want my espresso test script to verify that I'm not on production. Bad things happen if I run a purchase on production, let alone lots them..
I know in Java you need to add a -ae to run assertions. Which doesn't seem to be as simple in an android espresso test. I'll be handing this code off to the testers so I really really need it to fail if it's on the production. (obviously I'll wrap it in an IF, but I want it to be more ugly -- you messed up -- kinda thing.)
public class PurchaseTest extends BaseFooTest<HomeActivity> //ActivityInstrumentationTestCase2<LoginRegisterActivity>
{
final static String TAG = "PurchaseTest";
static final String PROD_URL = "https://api.foobar.com";
public PurchaseTest()
{
super(HomeActivity.class);
}
public void test()
{
System.out.println(fooApplication.hostUrl);
assert fooApplication.hostUrl.equalsIgnoreCase(PROD_URL) == false;
assert fooApplication.hostUrl.equalsIgnoreCase(PROD_URL) == true;
// No assert! Not being read then!
}
////////////////////// boss mans code, that the class is extending, I don't think it matter, but included it incase the extends basefootest confused someone.
public class BaseFooTest<T extends Activity> extends ActivityInstrumentationTestCase2
{
public BaseFooTest( Class<T> activityClass )
{
super( activityClass );
}
#Override
public void setUp() throws Exception
{
super.setUp();
getActivity();
tryClickOk();
}
protected ViewAssertion isDisplayed()
{
return ViewAssertions.matches( ViewMatchers.isDisplayed() );
}
protected void tryClickOk()
{
try
{
onView( withText( "OK" ) ).perform( click() );
}
catch ( NoMatchingViewException e )
{
// Eat it
// System.out.print( e );
}
}
}
It's possible to enable keyword asserts, but it requires a manual step, and it would be unusual to use keyword asserts in test code.
It's best to use the JUnit methods (assertTrue, etc.) as you would in unit tests.