JUnit4 parameterized test with different Android SDK versions - android

I'd like to test a function having a conditional statement for different Android SDK build versions with some parameters. The function looks like:
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) {
// do something with parameter(s) and return it;
} else {
// do something with parameter(s) and return it;
}
I can use Parameterized.class runner or JUnitParams to get it run with a set of parameters. I can also create a single test function and can change the SDK_INT field and call the function to test. But I was not able to combine both approaches into the same unit test class.
Is there any annotation to set the SDK for a particular test method? Or what is the best approach to test functions like the above?

A simple yet not elegant solution is to use the Enclosed runner and inner classes changing the SDK_INT field in a function annotated with #BeforeClass.

Related

How to conditionally skip a unit test in Android and kotlin?

I need to run a unit test based on whether this asset exists at runtime. The reason is, I am downloading the file in react native and in android, I am running some unit tests that requires this file.
I would like to run the unit test only if this file exists. Does anyone have any suggestions or code samples on how this can be achieved? Or is there another way these unit tests can be accomplished?
You shouldn't do that in a unit test because you want to have the file locally in your testing environment or have a mock that provides it. Otherwise you're not Eradicating Non-Determinism in Tests.
Let's assume you need to do something like that anyway. So, I would add a condition in the assert expression:
#Test
fun `Given a variable When has a value Then assert it has a value`(){
var myVar = null
myVar = getAValue()
myVar?.let {
assertNotEquals(null, myVar)
}
}
To me, eradicating non determinism in this particular context means that test scope has always specific expected values, the conditional expression in the let or an if enclosing an assert expression violates that. Hence the code above shouldn't be part of your tests. Instead, you have to write a test for the case myVar is null, you write another test for the case myVar is non null.
That's why I use Given, When, Then the conditional state would make the Given/When very messy.

Filtering Android Connected Tests

I have a few "connected" tests that are only relevant to run on a specific device model or on a specific brand and should be skipped on other brands/models.
I may be missing something, but this kind of filtering seems not possible out-of-the-box with AndroidJUnitRunner (by using annotation and/or passing appropriate arguments to it).
So, I was thinking to extend the AndroidX test framework to support this kind of filtering. In the end, I would like to be able to filter test with something like this
#TargetDeviceFilter(brand="SAMSUNG",model="XCover3")
#Test
public void myTestToRunOnSamsungXCover3DeviceOnly(){
...
}
My question: is there any way to accomplish this kind of filtering without extending AndroidX test framework? And if writing my own AndroidJUnitRunner and/or my own annotations is required, how should I start ?
I found a few interesting base classes that I may need to extend like :
androidx.test.internal.runner.TestRequestBuilder
androidx.test.internal.runner.TestRequestBuilder.DeviceBuild
but as those classes are in a "internal" package: attempting to extend them is probably not a good idea?
Any advice on how to deal with that problem is welcome.
I think, you may use org.junit.Assume.
Create a helper class DeviceHelper to detect mobile device informations for convenience.
Your test logic will be executed only if the assumption is correct.
#Test
public void myTestToRunOnSamsungXCover3DeviceOnly() {
// adapt this part to your business need
org.junit.Assume.assumeTrue(
DeviceHelper.isBrand("SAMSUNG") &&
DeviceHelper.isModel("XCover3")
);
// i.e. you can filter whatever you want test's according to device sdk_int
assumeTrue(SomeHelper.getDeviceSdk() >= 21);
// your test code
}

Android Unit Testing with Mockito

I have a ViewModel in which there is a method which has the following line of code:
billDate.set(!TextUtils.isEmpty(SampleApp.getInstance().getAccountManager().getDueDate()) ?
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),
SampleApp.getInstance().getAccountManager().getBillingDueDate()) :
SampleApp.getInstance().getApplicationContext().getString(R.string.missing_due_date));
I have a test class using Mockito to test the different methods in ViewModel. But it is failing with NullPointerException at this line:
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),
Below is the log:
java.lang.NullPointerException
at java.util.regex.Matcher.getTextLength(Matcher.java:1283)
at java.util.regex.Matcher.reset(Matcher.java:309)
at java.util.regex.Matcher.<init>(Matcher.java:229)
at java.util.regex.Pattern.matcher(Pattern.java:1093)
at java.util.Formatter.parse(Formatter.java:2547)
at java.util.Formatter.format(Formatter.java:2501)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
While running a test case, I see the log showing some error related to Pattern
Can somebody suggest, how to test the String.format() method?
First of all, you should not be importing android view packages into your ViewModel. So skip using things like TextUtils inside ViewModels.
As to the getApplicationContext().getString(), create an interface for this. Something like:
interface StringProvider {
String getString(int resource);
}
Then pass that interface in your ViewModel constructor and use that to get the string you want.
When you initialize the ViewModel, you can pass a concrete implementation of StringProvider like this:
class StringProviderImpl implements StringProvider {
String getString(int resource) {
return SampleApp.getInstance().getApplicationContext().getString(resource);
}
}
This way, for your unit tests, you can just mock StringProvider and don't have to worry about dealing with contexts inside your ViewModel and the related test code.
You don't need to test the String.format method. That is not your code, and your goal should be to test your own code. But your code is using that method, so you need to test your code. This is the part you are trying to validate or mock out as I understand it:
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due), SampleApp.getInstance().getAccountManager().getBillingDueDate())
which makes several calls to SampleApp to get an instance. Since those calls to SampleApp.getInstance are static method calls, you won't be able to mock them out. There isn't enough code posted to know what SampleApp is or what SampleApp.getInstance() returns or to know if any of the subsequent calls on that instance are returning null, but one of them is. So I think to solve this you need to look at the what the getInstance method returns. If you can't touch that code and you're hoping to only modify your test classes, you may not be able to test this with mockito due to the static method.
But otherwise you will need to build a way for your tests so the call to SampleApp.getInstance returns a mock object as the instance instead of whatever real instance I presume it is returning now. Then you can mock out the subsequent methods like getApplicationContext and getString to make them return canned responses so that the string.format call will not fail on a null input.
One note of caution--if you do end up making the static getInstance method return a mock, but sure you have proper cleanup when your test is done to set it back to what it was returning originally so you don't inadvertently modify something that might cause another unrelated unit test to fail. That is always a risk if you change something returned by a static method in a unit test since you are effectively changing it for all tests.
Considering that the test fails after the AccountManager was already used, you should have set up the SampleApp as a mock or fake already.
SampleApp app = SampleApp.getInstance()
AccountManager am = app.getAccountManager();
Context context = app.getApplicationContext();
billDate.set(!TextUtils.isEmpty(am.getDueDate()) ?
String.format(context.getString(R.string.due), am.getBillingDueDate()) :
context.getString(R.string.missing_due_date);
Now you only need to make sure to mock the Context you provide with with app.getApplicationContext() or the SampleApp itself, if you use app.getString() directly.
doReturn(dueFormatString).when(context).getString(R.string.due);
doReturn(dueMissingString).when(context).getString(R.string.missing_due_date);
But in general you should abstract the Context away. Not using it will simplify your code and therefore your testing a lot.
Also consider using context.getString() instead of String.format() for formatting a string you load from a resource. It's as easy as adding the format arguments as parameters to the call.
context.getString(R.string.due, am.getBillingDueDate())

How to write junit test for after getting response in Android

I have this below code.I want to write junit test for this method.
#Override
public void getSuccessData(Response response) {
if(response.getStatus().equalsIgnoreCase("success")){
BaseApplication.getInstance().setAccessToken(response.getToken().getAccessToken());
commonNavigate.navigateToHomeScreen((HomeActivity)view);
}
}
How can i write junit test case for this method.I am very new to junit.
This is (most probably) a callback method you want to test.
If you want to test a callback, you would need to understand mocking.
In very basic terms, mocking lets you create a fake source object and invoke some request method on it, and then verify that a particular callback has been invoked with certain parameters. Read about Mockito, which can be easily integrated with Android Studio: http://site.mockito.org/
Secondly, you code calls android-specific code:
BaseApplication.getInstance().setAccessToken(response.getToken().getAccessToken());
commonNavigate.navigateToHomeScreen((HomeActivity)view);
This code has dependency upon Context object. Please read what Context object means in Android and how it is shared in Application/Activity/View classes. "navigateToHomeScreen" method surely needs a Context!
Either you will mock android dependencies with fake objects, or you could run Instrumented tests which provide Context and other Android-framework-defendant objects.
To sum up - these are wide and complex topics and you should make a research on them first.
Use Mockito framework if you want to test methods. You need to mock objects so that you can test the method with dummy response.
Please refer this link for mockito
https://developer.android.com/training/testing/unit-testing/local-unit-tests.html#setup

Using multiple TestRules in the same test

I have written a custom TestRule to use with my Android test suite. It populates a table in the database used by the app under test. Now I need to use this DataRule along with ActivityTestRule. Can I have two fields of my test class annotated with #Rule? How do I control the order in which the rules are applied?
Background:
The Android API provides a TestRule for starting an Activity which is the core class for every app with a UI. My app has a database and I have several tests which require the database to be pre-populated with some known data. Previously, with JUnit3-based tests, I used an abstract superclass to centralize the code which prepares the database and then I extended this superclass for different test cases. Now I am trying to implement the same logic using JUnit 4. I learned recently that test rules are one way to provide logic which is reused across tests, so I am trying to move the logic from my superclass to a test rule. Is this an appropriate way to achieve my goal?
You certainly can have multiple #Rule fields in a single test. I'm not sure what the default ordering of rule application is, or if it's even well-defined. However, if ordering is important you can control it with a RuleChain
which allows you to define an order on how rules are applied when you have multiple rules in a test case.
From the Javadoc...
#Rule
public RuleChain chain = RuleChain
.outerRule(new LoggingRule("outer rule")
.around(new LoggingRule("middle rule")
.around(new LoggingRule("inner rule");
RuleChain is deprecated and since 4.13 you can make use of order parameter in Rule.
org.junit.Rule annotation has a parameter "order" which you can use to order the Rules in one file.
check the doc in the link below
Rule.java
If you're using JUnit for your tests, which I personally recommend, it's not recommended to have multiple rules in the same file, because a Rule is a unit of your test, and as you're doing unit tests, you should have just one Rule per file.
If you need to create some sort of data before you run your tests you should use the #Before and then load the necessary information.
More on this can be found here: http://junit.sourceforge.net/javadoc/org/junit/Before.html
If you have to load the same data in multiple classes, I would recommend you to create a class with your logic, extend that class in your test class and then create a method annotated with #Before an call your super class method.
Hope that helps

Categories

Resources