I'm trying to do some unit testing, and I haven't really settle yet on a runner or test class.
At the end, this is the method I'm verifying it works:
public static void getData(final Context context, final Callback<MyObject> callback) {
Locale locale = context.getResources().getConfiguration().locale;
MyObjectService myService = new MyObjectService(getRequestHeaders(context), locale);
}
So I need a mock context that has resources, configuration, locale and SharedPreferences.
I've tried PowerMockito, AndroidTestCase, AndroidTestRunner, ApplicationTestCase. All I can get is a mock Context with nothing in it, or mock Resources, but I can't figure out how to add them to the mock context (basically remake a Context).
This is currently my last attempt (although I've tried more complex ones, without any success):
import com.myApp.android.app.shop.util.ServiceUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.modules.junit4.PowerMockRunner;
import android.content.Context;
#RunWith(PowerMockRunner.class)
public class TestLogin
{
#Test
public void test()
{
Context context = Mockito.mock(Context.class);
ServiceUtils.getData(context, dataCallback);
}
}
The exception I get is a NPE from getData() as the passed context does not have resources.
Any suggestions?
Android is not an easily mockable system. I too have tried using Mockito to mock the framework, but it quickly becomes extremely complex. Things like resources and SharedPreferences are not easy to mock. These problems have been tackled in 3rd-party APIs like Robolectric.
I suggest you use Robolectric for your Android unit tests. Robolectric runs in a standard VM (i.e. no emulator needed). Robolectric simulates the majority of the Android system, thus minimizing (but not eliminating) the need to build mocks. I have found it to be a very useful tool for Android TDD.
As a second answer (and maybe more useful than the first) I suggest you to split the logics from the platform's dependencies.
In this way you can consider to unit test the logic in a pure-java way.
Take a look on MVP pattern. It should helps a lot!
In this case, for example, you would move this line...
MyObjectService myService = new MyObjectService(getRequestHeaders(context), locale);
... inside a "Service" that is completely a pure-java class allocated by a constructor that require (in this case) a String value called "locale" and a data structure called "headers"
Easy, no? ;)
My 2 cents: everytime that you need Robolectric to test logics (instead of UI) you probably make a mistake
Use the RuntimeEnvironment by Robolectric
Locale locale = RuntimeEnvironment.application.getResources().getConfiguration().locale;
Related
I could not find much resources on my question so I guess this is not an easy resolution.
We use JodaTime in our codebase and I wish to forbid (or at least warn) using some methods from this library as they are prone to errors (around timezone management).
I tried the reflections library already, without success due to a non released issue.
We used to have a custom sonar rule to handle this but it is not supported by sonarcloud so I looking for another way.
Do you have any lead to handle this?
I would recommend using ArchUnit for this, which allows you to specify restrictions such as this as unit tests:
public class DisallowedMethodsTest {
#Test
public void forbidJodaTimeMethods()
{
JavaClasses importedClasses = new ClassFileImporter().importPackages("your.base.package");
ArchRule rule = noClasses().should()
.callMethodWhere(target(name("disallowedMethodName"))
.and(target(owner(assignableTo(DateTime.class)))))
.because("Your reasons");
rule.check(importedClasses);
}
}
If you are looking for something works in unit test environment, Jeroen Steenbeeke' answer might be helpful.
If you are looking for something works in production environmen, you'll need HOOK.
In case you cannot require partners to use java.lang.reflect.Proxy to construct related object, I'd recommend you have a look on AspectJ if you are working on a regular Java project or Xposed if you are working on an Android project.
Both of them could add restrictions without modifing existing codebase nor programming flow.
I solved such kind of problems by writing an interceptor like the following, as explained at https://docs.oracle.com/javaee/7/tutorial/interceptors002.htm:
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class MethodCallTracerInterceptor {
#AroundInvoke
Object intercept(InvocationContext context)
throws Exception
{
Method method = context.getMethod();
String methodClass = method.getDeclaringClass().getName();
String methodName = method.getName();
if (methodClass.equals("myClass") && methodName.equals("myMethod")) {
//TODO Raise an exception or log a warning.
}
return context.proceed();
}
}
Currently the JUnit5 Framework works with Inversion of Control. I.e. you annotate a test method with #Test and then JUnit scans your classpath (in the simplest case)
Now is there a way for me to be in charge of calling the test cases through JUnit APIs? Maybe by hooking my test implementations to some test registry provided by JUnit?
I'm pretty new to JUnit - how did older versions go about this?
The reason I'm asking is that normally to execute my test cases, I'd have to run something along the lines of
java -jar junit-platform-standalone.jar --class-path target --scan-class-path
on the command line. My situation requires me to run the test cases through by executing one of my own classes, like that e.g.
java /com/example/MyTestCassesLauncher
EDIT: to clarify, I need one of my own classes to be hosting/launching my test cases, something like this:
// Maybe this needs to extend one of JUnit's launchers?
public class MyTestCassesLauncher {
public static void main(String[] args) {
JUnitLauncher.launchTests(new MyTestClass());
}
}
where JUnitLauncher.launchTests is some kind of API provided by the platform. I'm not looking for a method with that exact same signature but a mechanism that would allow me to ultimately call my own MyTestClassesLauncher class to run the tests.
Thanks in advance.
Not sure what you arę actually trying to achieve but in Junit5 to change behaviour of your tests you can use Extensions mechanism, similar to Junit4 RunWith but more powerful
Such custom extension can provide some additional logic like in this logging example
public class LoggingExtension implements
TestInstancePostProcessor {
#Override
public void postProcessTestInstance(Object testInstance,
ExtensionContext context) throws Exception {
Logger logger = LogManager.getLogger(testInstance.getClass());
testInstance.getClass()
.getMethod("setLogger", Logger.class)
.invoke(testInstance, logger);
}
}
The way Junit controls it's flow is Junit problem - you should not modify framework but extend it
When trying to perform test driven development on my JSF app, I have a hard time understanding how to make my classes more testable and decoupled.. For instance:
#Test
public void testViewDocumentReturnsServletPath(){
DocumentDO doc = new DocumentDO();
doc.setID(7L);
doc.setType(“PDF”);
DocumentHandler dh = new DocumentHandler(doc);
String servletPath = dh.viewDocument();
assertTrue(servletPath, contains(“../../pdf?path=“);
}
This is only testable (with my current knowledge) if I remove some of the supporting private methods inside viewDocument() that are meant to interact with external resources like the DB.
How can I unit test the public API with these supporting private methods inside as well?
Unit testing typically includes mocking of external dependencies that a function relies on in order to get a controlled output. This means that if your private method makes a call to an API you can use a framework like Mockito to force a specific return value which you can then use to assure your code handles the value the way you expect. In Mockito for example, this would look like:
when(someApiCall).thenReturn(someResource);
This same structure holds if you wish to interact with a database or any other external resource that the method you are testing does not control.
I have Java code that uses JAR:
public class Version {
public String getVersion() {
// Use Java Package API to return information specified in the manifest of this JAR.
return getClass().getPackage().getImplementationVersion();
}
}
How do I run JUnit test for this code?
It fails in development build (in Eclipse) since there is no JAR file yet.
It fails in production build (in Gradle) since there is no JAR file yet.
You always need to mock the dependencies for your unit testing. Boundary is unit test your code and not the jar itself. Mockito framework is good and there are other frameworks that do the job.
Chances are, that this can't be properly mocked (and thus: not unit tested). The point is that you are actually calling a method on "this". But you can't test some object ... and mock it at the same time.
You see, if your production code would look like this:
public String getVersion() {
return someObject.getClass().....
}
then you could create a mock object; and insert that into your Version class. But even then, the method getClass() is final within java.lang.Object; and therefore you can't be mocking it anyway.
[ Reasonable mocking frameworks like EasyMock or Mokito work by extending classes and overriding the methods you want to control. There are frameworks like PowerMock that do byte code manipulation and that allow for this kind of mocking - but you should never ever use such libraries; as they have really bad side effects (like breaking most coverage libraries) ]
What might work:
class Version {
private final Package packageForVersionCheck;
public Version() {
this(getClass().getPackage()));
}
Version(Package somePackage) {
this.packageForVersionCheck = ...
}
public String getVersion() {
return this.packageForVersionCheck.getImpl....
Now you can use dependency injection to provide a "mocked" package that returns that string. But well, that looks like a lot of code for almost no gain.
Long story short: sometimes, you simply can't write a reasonable unit test. Then do the next best thing: create some "functional" test that is automatically executed in a "customer like" setup; and make sure that you have an automated setup to run such tests, too.
I have a method that contains 2 conditions. In each condition the Logger.error method is invoked. The first test, that verifies the invocation of that method, succeeds, but any other test fails with
Wanted but not invoked...Actually, there were zero interactions with
this mock.
Does anyone know why this happens?
Below, I provided an example class and a unit test that will generate the issue:
package packageName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class X {
private static final Logger LOGGER = LoggerFactory.getLogger(X.class);
public void execute(boolean handle1stCase) {
if (handle1stCase) {
LOGGER.error("rumpampam");
} else {
LOGGER.error("latida");
}
}
}
The test:
package packageName;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
#RunWith(PowerMockRunner.class)
#PrepareForTest({LoggerFactory.class})
public class XTest {
#Mock
private Logger loggerMock;
private X x;
#Before
public void construct() {
MockitoAnnotations.initMocks(this);
mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
x = new X();
}
#Test
public void whenFirstCaseErrorLogged() throws Exception {
x.execute(true);
verify(loggerMock, times(1)).error("rumpampam");
}
#Test
public void whenSecondCaseErrorLogged() throws Exception {
x.execute(false);
verify(loggerMock, times(1)).error("latida");
}
}
The outcome:
Wanted but not invoked: loggerMock.error("latida");
-> at packageName.XTest.whenSecondCaseErrorLogged(XTest.java:51)
Actually, there were zero interactions with this mock.
EDIT:
I gave my short answer of why the every test except the 1st was failing in a comment of this answer.
MY SOLUTION TO THE PROBLEM:
In the test provide a:
public static Logger loggerMockStatic;
Than create only one instance for all the tests and provide it in the static variable, and use the static loggerMockStatic from than on. So you would have:
...
MockitoAnnotations.initMocks(this);
if (loggerMockStatic == null) {
loggerMockStatic = loggerMock;
}
mockStatic(LoggerFactory.class);
//when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMockStatic);
...
and use loggerMockStatic in the verify methods instead of loggerMock.
SOME THOUGHTS REGARDING APPROACH:
For me this is good because
1. it doesn't break design (if you considered that the needed variable should have been a constant, than it will stay this way).
2. its only 4 lines added in the test that will allow you to test the constant (in this case logger) behavior. Not much polluting and test case is still clear.
The "delete final and provide setter" approach as I explained in this answer opens the system to vulnerabilities. There is no need for someone to set the logger to the class, and I would always like the system to be as opened as needed. Providing a setter only for the need of a test is not desired. The test should work for the implementation, not the other way around.
Specifically on testing the logging, I don't consider that logging should be tested in general (most of the) cases. Logging should be an aspect of the application. And when you have other outputs to test for a certain path, than those outputs should be tested. But in this case (and maybe others) where there is no other output for a certain path, like logging and returning on a certain condition, testing the log (according to me) is needed. I want to always know that the log message will remain being logged even if someone changes the condition. If there was no log, and if someone changes the condition the wrong way there will be no way of knowing that the mistake resides in this piece of code (except maybe with debugging).
I was discussing with some colleagues that having a separate class for doing the logging would do the trick. That way the constant is isolated in another class, and you will be able to check the behavior with only Mockito. They made a further remark that this way if you would wanted to send the log to an email it would be easier to change.
First of all, I consider this a premature modularization IF you are not aiming in near future to switch between logging ways.
Secondly, using only Mockito + having another class and + 3 lines of code VS my one line of code (logger.error(...)) + using PowerMockito, I would again use the later. Adding additional dependencies during testing will not make you production code slower and bulkier. Perhaps when considering continues integration and that testing is also as important as other phases, you might say that this will make the process slower and bulkier while testing, but I would sacrifice that - it seems not too big of deal to me.
Your logger is static thus it's loaded when your class is loaded not when object is initialized. You have no guaratee that your mock will be ready on time, sometimes it might work sometimes not.
Here is why this is not working:
Field in class X is static and final which allows to set this only the first time class is loaded. This is dangerous because of what I wrote in my first answer. In your case you are lucky and this is not happening but...
Junit executes your test case in following order:
construct()
whenFirstCaseErrorLogged()
construct()
whenSecondCaseErrorLogged()
Now lets say that after first call to construct() XTest's field loggerMock is pointing to object which resides at the address 0001. This object is then used by LoggerFactory to initialize LOGGER field of x object. x.error is then called from whenFirstCaseErrorLogged() and that works finde because both loggerMock and X::Logger are pointing at the same object.
Now we get to the second construct(). Your loggerMock is reinitialized and now it points to a different object let assume that is stored in memory at the address 0002. This is a new object different from the previously created. Now because your X::LOGGER is static final it won't be reinitialized thus it still points at the object stored at the address 0001. When you will try to verify methods invoked on loggerMock you will get error because nothing was executed on that object instead error method of your previous object was called.
And here are some thoughts from me. Maybe they will appear helpful.
I think in future you should reconsider using static twice. Why do you want to make something constant when it is not constant ? Will your reference variable have the same value after you will run for the second time ? Of course it can happen but it is very unlikely. Can static final prevent you from changing the state of the object ? Of course not they will only prevent you from reassigning LOGGER to a different instance. You mentioned in your previous comment that you don't want a user of your code to provide null reference for your LOGGER. That is ok but you can prevent that by throwing an exception when one is provided or using a different null handling mechanism.
A lot has been said about using static keyword. Some consider it as pure evil some don't and some still love singletons :)
No matter what you think you have to know that static is no good for testing and threading.
I use static final when something is static like PI, or euler number but I don't use static final for objects that have mutable state. I use static methods for utility classes that don't store state but just do some processing ( parsing, counting etc.) and return the result imediatelly. A good example is a math function like power.
I think that will be useful ;)
Add a method to class X to allow the logger to be set, and remove the final from it. Then do something like this in the test.
#Mock private Logger mockLogger;
private X toTest = new X();
...
#Before
public void setUp() throws Exception {
toTest.setLogger(mockLogger);
}
#Test
public void logsRumpampamForFirstCall() throws Exception {
toTest.execute(true);
verify(mockLogger).error("rumpampam");
}
#Test
public void logsLatidaForOtherCalls() throws Exception {
toTest.execute(false);
verify(mockLogger).error("latida");
}