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.
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.
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.
}
}
}
}
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 need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.