I have created customized logging library named "StandardLogger" in which I am using logback for logging. Now I want to mock it in unit test but I am unable to do it.
Function I want to mock is static
public static String getHostOrDefault(String url, String defaultHost) {
try {
return new URL(url).getHost();
} catch (MalformedURLException e) {
logger.error("ConfigurationProvider", "getHostOrDefault", "Exception returning default host : "+ defaultHost + " Exception : "+e);
}
return defaultHost;
}
Class name and field
#Context
public class ConfigurationProvider {
private static final StandardLogger logger = new StandardLogger("CATS");
My Test class with unit test
class ConfigurationProviderTest {
#InjectMocks
ConfigurationProvider configurationProvider;
#Mock
private StandardLogger logger= new StandardLogger("CATS");
#Test
void thatGetHostOrDefaultReturnsHost() {
Mockito.doNothing().when(logger).info(Mockito.anyString(),Mockito.anyString(),Mockito.anyString());
String hostOrDefault = ConfigurationProvider.getHostOrDefault("http://abc.host:9433", "host");
assertThat(hostOrDefault, is("abc.host"));
}
Now when I am running it in debug mode logger object is NULL .
First of all, in the class under test you aren't injecting the logger. This means that you cannot inject it from the junit either. And exactly this happens: your logger is null in your class under test!
The second problem faces to you is about static methods. I've never been able to mock static method without using PowerMockito, even if here there is an interesting article that says that you can do it.
Related
I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}
The below test throws java.lang.IllegalStateException: no last call on a mock available when I don't extend from the PowerMockTestCase.
The error disappears as soon as I extend from PowerMockTestCase. Why exactly is this happening?
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMock;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest extends PowerMockTestCase{
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
long expectedId = 42;
// We create a new instance of test class under test as usually.
ServiceRegistartor tested = new ServiceRegistartor();
// This is the way to tell PowerMock to mock all static methods of a
// given class
PowerMock.mockStatic(IdGenerator.class);
/*
* The static method call to IdGenerator.generateNewId() expectation.
* This is why we need PowerMock.
*/
EasyMock.expect(IdGenerator.generateNewId()).andReturn(expectedId).once();
// Note how we replay the class, not the instance!
PowerMock.replay(IdGenerator.class);
long actualId = tested.registerService(new Object());
// Note how we verify the class, not the instance!
PowerMock.verify(IdGenerator.class);
// Assert that the ID is correct
assertEquals(expectedId, actualId);
}
}
While using PowerMock for static mocking, there is a class level instrumentation happening to make your mocking work. PowerMockTestCase class has a code (method beforePowerMockTestClass()) to switch your regular class loader to powermock class loader which orchestrates mocking injection. Hence you need to extend this class for static mock to work.
You need to have the PowerMock class-loaders configured so that the static classes can be intercepted (defined using the #PrepareForTest annotation).
You don't have to extend from PowerMockTestCase. For most cases you can also configure TestNG with a PowerMockObjectFactory instead:
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest {
#ObjectFactory
public IObjectFactory objectFactory() {
return new PowerMockObjectFactory();
}
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
...
}
}
I've been working with JMockit and its admittedly steep learning curve. I'm pretty new with mocking in general so please excuse the ignorance.
I'm trying to mock out the Logger interface so that I can verify the catch statement is working correctly. Call this an exercise in understanding how JMockit works. The implementing class for the Logger interface is Log4jLoggerAdapter so I thought if I passed an instance of that into my Expectations() block, JMockit would use dynamic partial mocking and "see" my logger statement. Instead, I get the following error:
mockit.internal.MissingInvocation: Missing invocation of: org.slf4j.impl.Log4jLoggerAdapter#error(String msg, Throwable t)
The Class Being Tested
public class MyLoggedClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLoggedClass.class);
... // Other variables
#Override
public void connect() {
String info = getServiceInfo();
try {
connector = MyConnectionFactory.connect(info);
} catch (Exception e) {
LOGGER.error("Exception connecting to your service with: " + info, e);
}
}
... // Other methods
}
My #Test
public class MyLoggedClassTest {
#Tested
MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(#Mocked final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class, logger){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
I'd detail the other approaches I've tried but this page would turn into a book. This is my best approach. Any ideas?
* Update * (yes, that was quick)
I changed #Mocked to #Cascading and removed the logger field from my Expectations signature and it worked. I don't understand why. Can someone please provide an explanation? Fumbling about until you stumble on something that works but you don't understand is not a recipe for success. See below:
#Test
public void myLoggingTest(#Cascading final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
No need for partial mocking in this case, just mock MyConnectionFactory in the usual way. The only tricky part is how to mock the class that implements the Logger interface, considering that it's instantiated from a static class initializer. As it happens, there is a feature in the mocking API for that (using JMockit 1.14):
public class MyLoggedClassTest
{
#Tested MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(
#Mocked MyConnectionFactory conFac,
#Capturing final Logger logger)
{
new Expectations() {{
MyConnectionFactory.connect(anyString);
result = new Exception();
}};
myLoggedClass.connect();
new Verifications() {{
logger.error(anyString, (Throwable)any);
}};
}
}
With a #Capturing mocked type, any implementation class will get mocked, so the test doesn't need to know about Log4jLoggerAdapter.
I am a Dagger newbie.
TL;DR:
If an Android Service has any fields injected into it using Dagger, then in order to actually perform the injection, I need to have an instance of that Service.
In Robolectric tests, this corresponds to MyService service = Robolectric.buildService(MyService.class).get(). And then, objectGraph.inject(service);
However, rest of the code that actually starts MyService still uses context.startService(context, MyService.class);.
Question: What is the idiomatic way in Dagger to address this mismatch?
Let's say I have a Service as follows:
public class MyService {
#Inject Parser parser;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String data = intent.getStringExtra("data_to_be_parsed");
parser.parse(data);
}
}
Elsewhere in my code, I have an ApiClient class that does this:
public class ApiClient{
public static void parseInBackground(Context context, String data){
//This service does not have its fields injected
context.startService(new Intent(context, MyService.class).putExtra("data_to_be_parsed", data));
}
}
That parseInBackground method will be called from an Activity in response to user interaction.
Now, I'm following TDD and hence, I haven't yet written the Application Module for this. Here's the test module:
#Module(injects = MyService.class)
public class TestModule {
#Provides #Singleton Parser provideParser(){
return new MockParser();
}
}
And finally, the test case:
#RunWith(Robolectric.class)
public class ApiTest {
#Test
public void parseInBackground_ParsesCorrectly(){
//This service has its fields injected
MyService service = Robolectric.buildService(MyService.class).get();
ObjectGraph.create(new TestModule()).inject(service);
ApiClient.parseInBackground(Robolectric.application, "<user><name>droid</name></user>");
//Asserts here
}
}
As you can see, in the test, I retrieve an instance of the service and then inject the MockParser into it. However, the ApiClient class directly starts the service using an Intent. I don't have a chance to perform the injection.
I am aware that I can have MyService perform an injection on itself:
public void onCreate(){
ObjectGraph.create(new TestModule()).inject(this);
}
But then, I am hardcoding the TestModule here.
Is there an existing idiom in Dagger to set up dependencies for such situations?
It's the wrong way to hardcode your modules either in tests or in services. Better approach is to perform creation via your custom Application object which in turn will hold singleton ObjectGraph object. For example:
// in MyService class
#Override public void onCreate() {
super.onCreate();
MyApp.from(context).inject(this);
}
// in MyApp class
public static MyApp from(Context context) {
return (MyApp) context.getApplicationContext();
}
//...
private ObjectGraph objectGraph;
#Override public void onCreate() {
// Perform Injection
objectGraph = ObjectGraph.create(getModules());
objectGraph.inject(this);
}
public void inject(Object object) {
objectGraph.inject(object);
}
protected Object[] getModules() {
// return concrete modules based on build type or any other conditions.
}
Alternatively, you can refactor last method out into separate class and make different implementations for different flavors or build types. Also you may want to set overrides=true in your TestModule's annotation.
I am using PowerMockito and jUnit to write unit test cases.
public class Foo {
private String resolveApplicationId() {
return "testApplication";
}
}
Here is my test case
#RunWith(PowerMockRunner.class)
#PrepareForTest(Foo.class)
public class test{
#Before
public void prepareTest() {
foo = PowerMockito.spy(new Foo());
}
#Test
public void checkApplicationIdIsResolved() throws Exception {
PowerMockito.doNothing().when(foo, "myPrivateMethod");
PowerMockito.verifyPrivate(foo).invoke("myPrivateMethod");
//Assert Here the returned value
}
}
Please tell me
1. how can I assert the value returned by the method when it is called
2. how can I call the private method
3. if not then what actually I verify when I write test case for private methods.
Thanks.
Testing private method does not differ from testing public method. If there is no external dependencies you even don't need to create and use any mocks. The only problem is with invocation of the private method from test. This is described here or you may use spring utils.
So you don't need to mock the method you are testing. You only require to mock other objects which are not tested in this particular test. So you test would look like
#Test
public void checkApplicationIdIsResolved() throws Exception {
// makeResolveIdAccessible();
// if needed setup mocks for objects used in resolveApplicationId
assertEquals(expectedApplicationId, foo.resolveApplicationId())
}