Mock Build Version with Mockito - android

My target is to mock Build.Version.SDK_INT with Mockito. Already tried:
final Build.VERSION buildVersion = Mockito.mock(Build.VERSION.class);
doReturn(buildVersion.getClass()).when(buildVersion).getClass();
doReturn(16).when(buildVersion.SDK_INT);
Problem is that: when requires method after mock, and .SDK_INT is not a method.

So far from other questions similar to this one it looks like you have to use reflection.
Stub value of Build.VERSION.SDK_INT in Local Unit Test
How to mock a static final variable using JUnit, EasyMock or PowerMock
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
...and then in this case use it like this...
setFinalStatic(Build.VERSION.class.getField("SDK_INT"), 16);
Another way around would be to create a class that accesses/wraps the field in a method that can be later mocked
public interface BuildVersionAccessor {
int getSDK_INT();
}
and then mocking that class/interface
BuildVersionAccessor buildVersion = mock(BuildVersionAccessor.class);
when(buildVersion.getSDK_INT()).thenReturn(16);

This works for me while using PowerMockito.
Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 16);
Don't forget to type
#PrepareForTest({Build.VERSION.class})

In case of java.lang.ExceptionInInitializerError , use 'SuppressStaticInitializationFor' to suppress any static blocks in the class. Usable example as follows:
#SuppressStaticInitializationFor({ "android.os.Build$VERSION", "SampleClassName" )}
Be careful inner class must use $ instead of Dot .

Related

How to mock static java method from kotlin , mockkStatic not working

I want to mock static method of java class from Kotlin test case.
I am using below code that does not work.
It always called actual method.
mockkStatic(Aes::class)
every { Aes.decrypt(PASSWORD, SECRET_KEY) } returns PASSWORD
Actual method in java class:
public static String decrypt(String text, String secretKey) {}
The good strategy for this is to use wrapper objects around static methods if there is no other way around (for example static method belongs to 3rd party library)
class AESWrapper {
fun decrypt(String text, String secretKey) {
return Aes.decrypt(text, secretKey)
}
}
There are other solutions like PowerMock, but then you need to use PowerMockRunner as I remember which can limit you in the future

How to use DEBUG flags in AOSP classes?

Sometimes in AOSP sources I see private static final boolean debug flag with false as ïts value. And there are debug output if this flag is true. Something like this:
private static final boolean DEBUG_DRAW = false;
private static final Paint DEBUG_DRAW_PAINT;
static {
DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
if (DEBUG_DRAW_PAINT != null) {
DEBUG_DRAW_PAINT.setAntiAlias(true);
DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
}
}
Who and how uses it? Is it possible to switch this flag somehow and take debug output of AOSP classes?
Everything is possible with Java and Reflection
Pros:
I don't think there is anything more powerfull than that
Cons:
This technique will be executed at runtime so any code executed before this (e.g. classes loading)... well, is already executed. So you won't see the effects
Slow at runtime
Dangerous. Use it carefully
You can modify any value using this:
Class<?> klass = ...
Field field = klass.getDeclaredField("DEBUG_DRAW");
field.setAccesible(true);
field.set(
null, // as this is an static attribute we don't need anything here...
true // set true as new value
);
As I stated before reflection is a dangerous technique so that snipped will throw several exceptions if used wrong so you will have to handle them

Receiving Mockito UnfinishedStubbingException for doNothing.when clause in Android Unit Test

Firstly, my setup is RXJava 1, Retrofit 2 and I'm using Java 7.
I have a method that, when it is called, will set an atomic boolean to true.
That method then calls a retrofit API.
Upon completion, timeout etc... the atomic boolean is reset to false.
So, I would therefore like to uni test that when I call my method, the Atomic Boolean is set to true.
So, I do the following:
assertFalse(orderUseCase.isOrderInProcess());
orderUseCase.execute(id, orderWrapper, ts);
assertTrue(orderUseCase.isOrderInProcess());
Test that the boolean is false.
Execute my use case
Test that the boolean is true.
Now, in order to perform the last test I need to ensure the API does nothing when it is called (the execute method will call the retrofit API.
To try and doNothing I am using the following line at the start of my test case.
doNothing().when(orderAPI.orderComplete(anyString(), any(OrderWrapper.class)));
I am however receiving the following error:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.tfds.xms.unit_test.SingleTest.TestAtomicBooleanLocked(SingleTest.java:90)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
From reading where other people had a similar issue, the answers were suggesting that their 'when' line was calling another Mock (which I don't believe I am doing) or that they hadn't done the 'PrepareForTest' step which I have done. I am however wondering if what is in my prepareForTest section is correct or not...
Incidentally I understand that the error is pointing me to add a 'thenReturn' but that would mean that the API 'does' something and would consequently cause the Atomic Boolean to unlock again, which is not what I want..
Any help is very much appreciated.
My full test case is:
#RunWith(TestRunner.class)
#PrepareForTest({OrderApiService.class, OrderUseCase.class})
public class SingleTest {
#Rule
public RxJavaResetRule pluginsReset = new RxJavaResetRule();
private OrderApiService orderAPI;
private OrderUseCase orderUseCase;
private OrderRepository orderRepository;
#Mock App app;
#Before
public void setUp() {
orderAPI = mock(OrderApiService.class);
orderRepository = new OrderRepository(app, orderAPI);
orderUseCase = new OrderUseCase(orderRepository);
}
#Test
public void TestAtomicBooleanLocked() throws Exception {
doNothing().when(orderAPI.orderComplete(anyString(), any(OrderWrapper.class)));
String id = "5";
Order order = new Order();
OrderWrapper orderWrapper = new orderWrapper(order);
TestSubscriber<GenericResponse> ts = new TestSubscriber<GenericResponse>();
assertFalse(orderUseCase.isOrderInProcess());
orderUseCase.execute(id, orderWrapper, ts);
assertTrue(orderUseCase.isOrderInProcess());
}
}
The doNothing() method from Mockito works a bit different then the standalone when() method. You should be fine by just pushing the method call out of the .doNothing() like this:
doNothing().when(orderAPI)
.orderComplete(anyString(), any(OrderWrapper.class));
Happy testing!

How to handle logging in Android JUnit tests?

I would like to log information in model classes - not necessarily for unit testing purposes but for real life scenarios where I am trying to debug.
However, if I try to use android.util.Log methods I get the following errors when running JUnit tests:
java.lang.RuntimeException: Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
I understand why this occurs, I should not be using Android framework code in model classes that are designed to be framework independent! I'm not really arguing against the error, but rather I am trying to find a way to work around this.
I have one idea, does this make sense?
Create a CustomLog class along these lines:
public class CustomLog {
private static ILogger mLogger;
public static void setLogger(ILogger logger) {
mLogger = logger;
}
public static void e(String tag, String message) {
mLogger.e(tag, message);
}
}
Where ILogger is an interface with the required methods to perform the log functionality (e, d, etc. methods...)
I could create an ILoggerImpl that uses the android.util.Log methods, and a MockLogger class that simply prints out to System.out.println and/or does nothing (or anything else!).
I think that'd perfectly fit my needs (I would be required to setup my CustomLog class very early on in the lifecycle, but that's not a huge deal).
However, if I ever needed to add third party libraries/outside code to my model classes, this would likely break again in the same manner if the new libraries/code use android.util.Log methods.
So, is there a "catch all" type behavior I could use? What do you think?
One way of solving the "Not mocked" exception you cited is to use PowerMockito to mock the Logging methods. Instead of calling PowerMockito.mockStatic(Log.class); as explained in the linked answer, you can take inspiration from this, and use PowerMockito to replace() Android.util's Log.v, Log.d, Log.i & Log.e with methods that will run System.out.println instead. This allows you to see the logged messages in Android Studio's Run window.
String[] logMethods = {"v", "d", "i", "e"};
InvocationHandler systemOutPrintln = new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
StringBuilder messageBuilder = new StringBuilder();
for (int i = 0; i < args.length; i++) {
String arg = args[i].toString();
messageBuilder.append(arg);
// add separators, ASCII art, ...
}
System.out.println(messageBuilder);
return messageBuilder.toString().length();
}
};
for (String logMethod : logMethods) {
replace(method(Log.class, logMethod, String.class, String.class)).with(systemOutPrintln);
replace(method(Log.class, logMethod, String.class, String.class, Throwable.class)).with(systemOutPrintln);
}
Disclaimer: I'm not sure if the above is the most idiomatic implementation.

Shadowing TimeZone.getDefault using Robolectric

I'm having some troubles shadowing TimeZone.getDefault() using Robolectric since my AppTest.class is not using my static shadow mwthod in ShadowTimeZone.class.
AppTest.class
#RunWith(RobolectricTestRunner.class)
#Config(manifest = "../App/AndroidManifest.xml")
public class AppTest{
#Test
#Config(shadows = {ShadowTimeZone.class})
public void testTimeZone() {
String expectedTimeZoneId = "Europe/London";
TimeZone timeZone = TimeZone.getDefault();
assertThat(timeZone.getID(), equalTo(expectedTimeZoneId));
}
}
ShadowTimeZone.class
#Implements(TimeZone.class)
public class ShadowTimeZone {
#Implementation
public static TimeZone getDefault() {
return TimeZone.getTimeZone("Europe/London");
}
}
You don't need to use a shadow at all. Use TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")) before your test or in a #Before setup method.
If you still want to use a shadow, the actual signature for getDefault is public static synchronized, so you may need to add synchronized to your shadow method to match.
By default you can only shadow classes from android package. But you can add more classes to work with shadows. See https://stackoverflow.com/a/29641926/3619179

Categories

Resources