With Android 6.0 and new permission model, I am checking if the permission exists before performing certain task.
I want to assign these permissions to available and not available for testing purpose. I have a static class to check various permissions depending on the string.
boolean result = ContextCompat.checkSelfPermission(context, name) == PackageManager.PERMISSION_GRANTED;
Can it be achieved using Mockito or Roboelectric?
If you move your permission checker to a collaborator class, you can mock the collaborator. I am not familiar with the Android return types but the solution would look like this:
class PermissionsChecker {
public String checkSelfPermission(context, name) {
return ContextCompat.checkSelfPermission(context, name);
}
}
class YourApp {
private PermissionsChecker permissionsChecker; //need to inject this via setter or constructor
public doSomething() {
boolean result = permissionsChecker.checkSelfPermission(context, name).equals(PackageManager.PERMISSION_GRANTED);
}
}
class YourAppTest {
PermissionsChecker permissionsChecker = mock(PermissionsChecker.class);
#InjectMocks YourApp app = new YourApp();
#Test
public void hasPermissions() {
when(permissionsChecker.checkSelfPermission(...)).thenReturn("NOT GRANTED");
app.something();
//verify you get some error
}
}
Related
Which type of variable can pass REQUESTED_PERMISSION to the following function (sample code from Here) and how it shoud be concatenated to android.Manifest.permission.+ ?
Edit:
It seems that the variable should also pass through the ContextCompat.checkSelfPermission (It is marked as unused). How should I pass it?
Public boolean checkPermission( ????? REQUESTED_PERMISSION){
if (ContextCompat.checkSelfPermission(
CONTEXT, android.Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
showInContextUI(...);
} else {
requestPermissions(...);
}
}
https://developer.android.com/reference/android/Manifest.permission
Here you have list with the permissions. They are all strings.
public boolean checkPermission( String permission ) { ... }
usage (for example, you need vibration) :
checkPermission(Manifest.permission.VIBRATE);
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.
}
}
}
}
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 have an app that uses custom Exceptions, such as this:
public class SomeException extends Exception{
private int iCode;
private String iMessage;
public SomeException(){
iCode = 201;
iMessage = **//Get the localized string R.string.error_201??**
}
#Override
public String getMessage() {
return iMessage;
}
#Override
public int getCode() {
return iCode;
}
}
Obviously, I want lo localize the error message. I have possible solutions but non of them satisfy me.
1) Pass "Context" to the constructor, and do ctx.getString(R.string.error_201)
--> Fail, as this Exceptions are sometimes thrown from MODEL classes, so they don't have a Context
2) Pass "Context" when retriveing the message in getMessage() function,
--> Fail, It's necesary to override the super method, to work as all other Exceptions.
Solution I have now: All activities in my app have this onCreate:
public void onCreate(...){
Utils.RESOURCES = getResources();
...
}
Very dirty code... I don't like the solution. My question is then,: is there a way to access the resources without the Context? And most important, How would an application such as mine solve this problem?
What about
public class MyException extends Exception {
private int iCode;
public MyException(int code) {
this.iCode = code;
}
#Override
public String getMessage() {
return "MyException code " + String.valueOf(iCode);
}
public String getLocalizedMessage(Context ctx) {
String message;
if (iCode == 201)
message = ctx.getString(R.string.error_201);
else if (iCode == 202)
message = ctx.getString(R.string.error_202);
// ...
}
}
Even if there was way to access context in different way, you should not do it. If you need to emit exceptions where you cannot pass Context, you should be able to access context before you display such error. I cannot see reason why you should create localized error messages from constructor. You can log to logcat not localized versions if you need. And where you want to display something in UI, you should have context at hand.
You can access only system wide resources without Context.
You need a Context, so I would suggest You to get it as soon as possible, and make it available through a static method or variable. You do the same thing in every Activity, but there is a cleaner method. You should make a custom Application, and override its onCreate() to make the resources public:
public class App extends Application {
private static Resources myResources;
#Override
public void onCreate() {
myResources = getBaseContext().getResources();
super.onCreate();
}
public static Resources getMyResources(){
return myResources;
}
}
The other thing you have to do is to set the Application in your manifest:
<application
android:name="{your_package}.App"
...
Now you can access the resources in all of your Activity without any preparation. Your custom Exception class could also use the externalized resources.