I have this below method.
protected static String encode(String url) {
try {
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOGGER.warn("exception occured while encoding url {}", url);
}
return url;
}
I am unable to provide a junit test for this because I can't mock URLEncoder. There are 2 possible outcomes of this method
encoded url
original url if there is some exceptions
I am able to create a test method for first outcome. how would you create test method for the second outcome?
The fundamental theorem of software engineering (FTSE) is a term originated by Andrew Koenig to describe a remark by Butler Lampson attributed to the late David J. Wheeler:
"We can solve any problem by introducing an extra level of indirection."
[...]
The theorem is often expanded by the humorous clause "…except for the problem of too many levels of indirection," referring to the fact that too many abstractions may create intrinsic complexity issues of their own. (Source: Wikipedia)
So let's say there's a class that has a static method named encode:
public final class UrlHelper {
protected static String encode(String url) {
try {
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOGGER.warn("exception occured while encoding url {}", url);
}
return url;
}
}
and your code depends on it:
public class MyClass {
public void doSomething(String someUrl) {
// ...
String encodedUrl = UrlHelper.encode(someUrl);
// ...
}
}
and you want to test MyClass.doSomething(String someUrl) but you want to mock UrlHelper.encode(someUrl). One option is to define another class such as
public final class MyUrlHelper {
protected String encode(String url) {
return UrlHelper.encode(someUrl);
}
}
As MyUrlHelper.encode(String url) is not static, you can refactor your original code and test it by relying on dependency injection and mocking the non-static MyUrlHelper.encode(String url):
// Refactored
public class MyClass {
private MyUrlHelper myUrlHelper;
public UrlHelper(MyUrlHelper muUrlHelper) {
this.myUrlHelper = myUrlHelper;
}
public void doSomething(String someUrl) {
// ...
String encodedUrl = myUrlHelper.encode(someUrl);
// ...
}
}
// Test
#Test
public void myTest() {
// setup myUrlHelper and configure it
MyUrlHelper myUrlHelper = mock(MyUrlHelper.class);
when(myUrlHelper.encode(...)).thenReturn(...);
// inject
MyClass myObject = new MyClass(myUrlHelper);
// stimulate
myObject.doSomething("...")
}
Another option is to use Mockito using the PowerMockRunner as explained by #Marimuthu Madasamy.
However, I don't see any benefit in mocking UrlHelper.encode or URLEncoder.encode here. It is not an external system (a database, a file system, a message broker, a SOAP API, a REST API, etc.) so I don't see any gains by mocking it.
You could use PowerMockito to mock static methods. Assuming the static method in your post is in a class called HelloWorld, here are the two tests where the first test is testing the positive case and the second test
is testing the exception case:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.powermock.api.mockito.PowerMockito.doThrow;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(PowerMockRunner.class)
#PrepareForTest(HelloWorld.class)
public class HelloWorldTest {
#Test
public void encode_returnsEncoded() throws UnsupportedEncodingException {
// given
mockStatic(URLEncoder.class);
when(URLEncoder.encode(any(String.class), any(String.class)))
.thenReturn("testUrlEncoded");
// when
String encoded = HelloWorld.encode("testUrl");
// then
assertThat(encoded).isEqualTo("testUrlEncoded");
}
#Test
public void encode_returnsInputOnException() {
// given
mockStatic(URLEncoder.class);
doThrow(new Exception("exception from test"))
.when(URLEncoder.class);
// when
String encoded = HelloWorld.encode("testUrl");
// then
assertThat(encoded).isEqualTo("testUrl");
}
}
If you are willing to use Lombok, I got a practical approach for you:
#lombok.Generated // Function won't raise up in Jacoco coverage report, see https://stackoverflow.com/a/56327700/1645517
#lombok.SneakyThrows(UnsupportedEncodingException.class) // Suppress handling this exception, see https://projectlombok.org/features/SneakyThrows
private static String urlEncode(final String valueToEncode) {
return URLEncoder.encode(valueToEncode, StandardCharsets.UTF_8.name());
}
Jacoco will ignore this method
The UnsupportedEncodingException cannot be thrown here. Let Lombok handle this ugly circumstance.
Related
Am trying to create a JUnit 5 test for a published .NET Soap Web Service using Java 1.8.
Currently, I am following a pattern from a WebServiceClient in the codebase which contains a main() method.
WebServiceClient.java:
import static com.google.common.base.Preconditions.checkNotNull;
public class WebServiceClient {
IPersonWebService service;
public WebServiceClient(IPersonWebService service) {
this.service = checkNotNull(service);
}
private PersonData getPersonData() throws Exception {
return service.getPersons(null);
}
public static void main(String [] args) {
IPersonWebService service = new IPersonWebServiceImpl().getBasicHttpBindingIPersonWebServiceImpl();
WebServiceClient client = new WebServiceClient(service);
PersonData personData = client.getPersonData();
System.out.println(personData.toString());
}
}
Need to following the same type of functionality in:
WebServiceTest.java:
import org.junit.Before;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.google.common.base.Preconditions.checkNotNull;
public class WebServiceTest {
IPersonWebService service;
public WebServiceTest(IPersonWebService service) {
this.service = checkNotNull(service);
}
#Before
public void before(IPersonWebService service) {
this.service = checkNotNull(service);
}
#Test
public void testGetPersonData() throws Exception {
IPersonWebService service =
new IPersonWebServiceImpl().getBasicHttpBindingIPersonWebServiceImpl();
WebServiceTest client = new WebServiceTest(service);
PersonData personData = client.getPersonData();
assertThat(personData).isNotNull();
}
private PersonData getPersonData() throws Exception {
return service.getPersonData(null);
}
}
Running this within IntelliJ IDEA results in:
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [com.myapp.IPersonWebService arg0] in constructor [public com.myapp.WebServiceTest(com.myapp.IPersonWebService)].
at java.util.Optional.orElseGet(Optional.java:267)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at java.util.ArrayList.forEach(ArrayList.java:1249)
IPersonWebService.java:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
#WebService(name = "IPersonWebService", targetNamespace = "http://sample.org/")
#XmlSeeAlso({
ObjectFactory.class
})
public interface IPersonWebService {
#WebMethod(operationName = "GetPersonData",
action = "http://sample.org/IPersonWebService/GetPersonData")
#WebResult(name = "GetVehiclesResult",
targetNamespace = "http://sample.org/")
#RequestWrapper(localName = "GetPersonData",
targetNamespace = "http://sample.org/",
className = "com.myapp.GetPersonData")
#ResponseWrapper(localName = "GetPersonDataResponse",
targetNamespace = "http://sample.org/",
className = "com.myapp.GetPersonDataResponse")
public {PersonData} getPersonData(
#WebParam(name = "applicationID",
targetNamespace = "http://sample.org/")
String applicationID)
throws IPersonWebServicetExceptionFaultMessage;
}
This contains the actual WSDL that will be imported into memory.
IPersonWebServiceImpl.java:
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
#WebServiceClient(name = "IPersonWebServiceImpl",
targetNamespace = "http://sample.org/",
wsdlLocation = "https://sample.com/WCF/IPersonWebServiceImpl.svc?singleWsdl")
public class IPersonWebServiceImpl
extends Service
{
private final static URL IPersonWebServiceImpl_WSDL_LOCATION;
private final static WebServiceException IPersonWebServiceImpl_EXCEPTION;
private final static QName IPersonWebServiceImpl_QNAME = new QName("http://sample.org/", "IPersonWebServiceImpl");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("https://sample.com/WCF/IPersonWebServiceImpl.svc?singleWsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
IPersonWebServiceImpl_WSDL_LOCATION = url;
IPersonWebServiceImpl_EXCEPTION = e;
}
public IPersonWebServiceImpl() {
super(__getWsdlLocation(), IPersonWebServiceImpl_QNAME);
}
public IPersonWebServiceImpl(final WebServiceFeature... features) {
super(__getWsdlLocation(), IPersonWebServiceImpl_QNAME, features);
}
public IPersonWebServiceImpl(final URL wsdlLocation) {
super(wsdlLocation, IPersonWebServiceImpl_QNAME);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final WebServiceFeature... features) {
super(wsdlLocation, IPersonWebServiceImpl_QNAME, features);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final QName serviceName) {
super(wsdlLocation, serviceName);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final QName serviceName,
final WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
#WebEndpoint(name = "BasicHttpBinding_IPersonWebServiceImpl")
public IPersonWebServiceImpl getBasicHttpBindingIPersonWebServiceImpl() {
return super.getPort(new QName("http://sample.org/",
"BasicHttpBinding_IPersonWebServiceImpl"),
IPersonWebServiceImpl.class);
}
#WebEndpoint(name = "BasicHttpBinding_IPersonWebServiceImpl")
public IPersonWebServiceImpl getBasicHttpBindingIPersonWebServiceImpl(final WebServiceFeature... features) {
return super.getPort(new QName("http://sample.org/",
"BasicHttpBinding_IPersonWebServiceImpl"),
IPersonWebServiceImpl.class, features);
}
private static URL __getWsdlLocation() {
if (IPersonWebServiceImpl_EXCEPTION!= null) {
throw IPersonWebServiceImpl_EXCEPTION;
}
return IPersonWebServiceImpl_WSDL_LOCATION;
}
}
Question(s):
When trying to follow along using the pattern inside WebServiceClient.java, why does WebServiceTest.java fail with this:
org.junit.jupiter.api.extension.ParameterResolutionException:
No ParameterResolver registered for parameter...
How to resolve this?
The WebServiceClient.java works and shows PersonData which was obtained from the IPersonWebServiceImpl.java (notice how this has the WSDL explicitly setup inside the static clause).
Is there a 3rd party open source Java framework which I can use to import any type of WSDL (.NET or others) and test SOAP based endpoints using JUnit 5?
Come from a RESTful Web Services background and not a SOAP based background, so any suggestions would be most appreciated.
So firstly, thanks for providing loads of code and making your question clear. I see it's been a few months since you asked, but I'll try and help.
I think there's two problems: the exception (tldr it's a junit version clash), and structuring code for writing unit tests.
ParameterResolutionException
#Test is imported from JUnit5, aka 'junit-jupiter', but #Before is from JUnit4, aka 'junit-vintage' or 'junit'.
import org.junit.Before; // junit4
import org.junit.jupiter.api.BeforeAll; // junit5
import org.junit.jupiter.api.Test; // junit5
Because the #Test is from junit-jupiter, the tests are run with JUnit5, which (unlike JUnit4) allows for parameters in test and constructors methods. WebServiceTest's constructor needs a parameter. But you don't have a provider for IPersonWebService, so JUnit5 breaks - it can't resolve it.
So to fix this:
remove the constructor
remove the parameter from the before method
change #Before to #BeforeEach (and fix the imports)
I'd also recommend (if possible) that you exclude any JUnit4 dependencies that you might have added (junit-vintage is for backwards compatibility), or might have been snuck in from other dependencies. It prevents confusions like this.
For example with maven, to see which JUnit versions there are, and their origins, view and filter the dependency tree:
mvn dependency:tree -Dincludes="*junit*"
Writing unit tests
I'm not sure what the application is intended to be, but (as I think you've found out) it's difficult to test. It certainly looks strange to me. WebServiceClient has a main method (I guess it gets called directly?) that immediately creates the API and calls it and returns the result - so there's no separation between the 'business logic' and 'api'. Maybe that main method is there for demonstrating?
It would be best to split things up. Hopefully then it makes testing more clear.
Let's make a dedicated WebServiceClient. This should be the only class that interacts with IPersonWebService.
There shouldn't be any sort of fancy logic in here, just simple API calls with basic exception handling.
// import generated soap stuff
public class WebServiceClient {
private final IPersonWebService service;
public WebServiceClient(IPersonWebService service) {
// an instance of the API is received in the constructor
this.service = service;
}
private PersonsData getPersonData() throws WebServiceClientException {
try {
// call the API
PersonsData persons = service.getPersonData(null);
if (personsData == null) {
throw new WebServiceClientException("Received null response from PersonsData :(");
}
return persons;
} catch (IPersonWebServicetExceptionFaultMessage e) {
// wrap the SOAP exception in our own wrapper
// it's best not to let SOAP code spread into our project.
throw new WebServiceClientException("Tried fetching PersonsData, but got exception from API", e);
}
}
}
And here's the test class for that service.
Each #Test method is completely independent from the others, it has its own little bubble to keep the tests independent.
I really recommend using a mocking framework, like Mockito, to create dummy instances. It's really powerful. In this case it means we can easily test exceptions. Mockito 3 works really well with JUnit5 - I've used the parameter injection here to make mocks for the tests (Mockito provides a Parameter Resolver under the hood).
#ExtendWith(MockitoExtension.class)
class WebServiceClientTest {
#Test
public void testWebServiceClientCreated(
// it's not actually important about the internal workings of IPersonWebService
// so use mockito to mock it!
// all we care about is that if it's there, then WebServiceClient can be created
#Mock
IPersonWebService service) {
WebServiceClient wsc = new WebServiceClient(service);
assertNotNull(wsc);
}
// test happy flow
#Test
public void testPersonsDataReturned(
// again, we don't care about the internals of IPersonWebService, just that it returns data
// so mock it!
#Mock
IPersonWebService service,
// it's also not important about the internals of PersonsData,
// just that it's an object that's returned
#Mock
PersonsData personsDataMock) {
// set up the mock
when(service.getPersonsData(isNull())).thenReturn(personsDataMock);
WebServiceClient wsc = new WebServiceClient(service);
// we don't need to check WebServiceClient here! We already have a test that does this.
// each test should only focus on one thing
// assertNotNull(wsc);
PersonsData result = wsc.getPersonsData();
// now we can check the result
assertNotNull(result);
assertEquals(personsDataMock, result);
}
// again, testing for failure is much more interesting than happy flows
// in this case, the test will fail!
// we'd better fix WebServiceClient to handle unexpected exceptions from IPersonWebService
#Test
public void testWhenApiThrowsNullPointerExceptionExpectWebServiceClientException(
#Mock
IPersonWebService service) {
// mock throwing an exception
NullPointerException npe = new NullPointerException("Another one of those weird external webservice exceptions");
doThrow()
.when(service.getPersonsData(isNull()));
WebServiceClient wsc = new WebServiceClient(service);
WebServiceClientException thrownException = assertThrows(WebServiceClientException.class,
() -> wsc.getPersonsData()
);
// now we can check the result
assertNotNull(thrownException);
assertEquals("Tried fetching PersonsData, but got exception from API", thrownException.getMessage());
assertEquals(npe, thrownException.getCause());
}
}
And a nice wrapper for any exceptions when dealing with the API.
class WebServiceClientException extends Exception {
public WebServiceClientException(String message) {
super(message);
}
public WebServiceClientException(String message, Exception cause) {
super(message, cause);
}
}
I have a class:
public class RequestHandler implements HttpHandler {
public void handleRequest(HttpServerExchange serverContext) throws Exception {
serverContext.dispatch(() -> serverContext.getRequestReceiver()
.receiveFullBytes((httpServerExchange, reqBytes) -> {
// business logic along with few function call
}
)
);
}
}
I want to write a unit test case to test my business logic. I am not sure how to do it with 2 levels of a lambda expression insider a dispatcher? Can someone please suggest a good way to write test cases?
I know that we can move business logic to new class and can test it (i guess it's better designed) but curious to know what if it's part of some legacy code or something that we can't change, how can we test it?
Under the assumption that somewhere in your buisness logic you forward the received message (or whatever you do with it) to somewhere else, you can just test your code as usual.
Note that HttpServerExchange is a final class, so you need to use a Mockito version that supports final mocking - and you have to enable it, as described here.
To get around the lambda expression you need to use thenAnswer or doAnswer to trigger the invocation of the correct interface method manually.
A simple example could look like this:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import io.undertow.io.Receiver;
import io.undertow.io.Receiver.FullBytesCallback;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
#ExtendWith(MockitoExtension.class)
public class RequestHandlerTest {
static class BuisnessLogic {
public void someMethod(HttpServerExchange httpServerExchange, byte[] reqBytes) {
}
}
static class RequestHandler implements HttpHandler {
BuisnessLogic logic;
public void handleRequest(HttpServerExchange serverContext) throws Exception {
serverContext.dispatch(
() -> serverContext.getRequestReceiver().receiveFullBytes(
(httpServerExchange, reqBytes) -> {
logic.someMethod(httpServerExchange, reqBytes);
}
)
);
}
}
#Mock
BuisnessLogic logic;
#InjectMocks
RequestHandler handler;
#Test
public void test() throws Exception {
byte[] message = new byte[] {1,2,3};
HttpServerExchange serverContext = Mockito.mock(HttpServerExchange.class);
// 1st lambda
Mockito.when(serverContext.dispatch(Mockito.any(Runnable.class)))
.thenAnswer((Answer<HttpServerExchange>) invocation -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return serverContext;
});
// 2nd lambda
Receiver receiver = Mockito.mock(Receiver.class);
Mockito.doAnswer((Answer<Void>) invocation -> {
FullBytesCallback callback = invocation.getArgument(0);
callback.handle(serverContext, message);
return null;
}).when(receiver).receiveFullBytes(Mockito.any(FullBytesCallback.class));
Mockito.when(serverContext.getRequestReceiver()).thenReturn(receiver);
// class under test - method invocation
handler.handleRequest(serverContext);
// buisness logic call verification
ArgumentCaptor<HttpServerExchange> captor1 = ArgumentCaptor.forClass(HttpServerExchange.class);
ArgumentCaptor<byte[]> captor2 = ArgumentCaptor.forClass(byte[].class);
Mockito.verify(logic).someMethod(captor1.capture(), captor2.capture());
Assertions.assertEquals(serverContext, captor1.getValue());
Assertions.assertEquals(message, captor2.getValue());
}
}
As others already mentioned you should only use that approach for legacy code.
A simple refactoring could just push the entire part you need to test into its own method, which - in the example above - would just be the buisness logic itself.
There is no explicit need to test the undertow framework yourself.
Consider the following field and method from a class i need to test.
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
The UUID is stored in a file that is created on the application's first run. A file.txt exists in the location indicated by pathToUUID. I am trying (and struggling) to write a unit test for this method.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
#Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Unfortunately when().thenReturn() is not called during testing and the test performs as an integration test, reading the file from the file system and returning its value, rather simply than the mock value i expect. However, if i spoof a call to Files.readAllBytes() in the test method and echo the result to the console, the expected value displays.
So, how can i get my method under test to properly function with the PowerMock when()-thenReturn() pattern?
For anyone facing a similar problem, i solved this by making the following changes to my test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\\DIRECTORY");
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
#Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Your class that you need to test is written in a bad way. The path shouldn't be hard coded - make it parametrizable - for example inject the path via the constructor. Then, in your integration tests just inject the path to your test resources and you're ready to go. No PowerMock, no hacks - simple constructor injection.
JDK classes are hard to deal with when using PowerMock. Here's what I would do in your case:
Refactor UUIDGetter to add a constructor for testing purposes that accepts the path to the "uuid" file:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
then test it like this:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
#Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
With a resource file (in test resources folder) named "so37059406/uuid.txt" and containing (no end-of-line):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
This is IMHO, way better, because:
No powermock: it's a powerful tool but it comes with a price (slower tests, possible tests strange interactions
It's more readable / easy to understand
I started to use Guava Optional as a part of the null object pattern and would like to improve the use in Mockito, where null is the default return value for mocked objects. To behave correctly one needs to explicitly tell Mockito to use Optional.absent() instead:
import org.mockito.*;
import org.testng.*;
import org.testng.annotations.*;
import com.google.common.base.Optional;
public class Example {
#Mock
private MyObject underTest;
#Test
public void testExample() {
// fails
// assertNotNull(underTest.get());
Mockito.when(underTest.get()).thenReturn(Optional.absent());
Assert.assertNotNull(underTest.get());
}
public class MyObject {
public Optional<Object> get() {
return Optional.absent();
}
}
#BeforeClass
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
}
Is there a easy way to improve mockito to automatically return Optional.absent() instead of null if the actual type is Optional?
I tried to solve it with reflection in #Before annotated method, however, I didn't manage to get it working.
Solution you found out can be improved by creating your own static factory that creates mocks with an OptionalAnswer and use it instead of default Mockito factory:
class MockitoOptional{
public static <T> T mock(Class<T> classToMock) {
return Mockito.mock(classToMock, new OptionalAnswer());
}
}
Next step will be to extend a test runner that will use this factory to inject mocks into #Mock annotated fields. Search for custom JUnit test runners if you haven't heard of them yet.
I got a first shot with the linked answer for strings.
public class OptionalAnswer extends ReturnsEmptyValues {
#Override
public Object answer(InvocationOnMock invocation) {
Object answer = super.answer(invocation);
if (answer != null) {
return answer;
}
Class<?> returnType = invocation.getMethod().getReturnType();
if (returnType == Optional.class) {
return Optional.absent();
}
return null;
}
}
#Test
public void testExample() {
MyObject test = mock(MyObject.class, new OptionalAnswer());
Assert.assertNotNull(test.get());
}
Won't get much easier, right?
Mockito.when(underTest.get()).thenReturn(Optional.<Object>absent());
This is all you need to do. Add the type returned from underTest.get() to your absent() call. Yes it is supposed to be on that side of the period.
public void sendMail() {
/* I have simplified the method for here */
Transport t;
t.send();
}
When I am writing unit test for this, since I don't want to actually send the mail, I want to mock the Transport class. Is it possible and if so, how to do it ?
'cglib' may fits.
Use ‘Enhancer' to proxy the 'Transport' class. In order not to actually sending the mail, you need pass into the 'Enhancer' a 'MethodInterceptor' which will not invoke the 'send()' method in super class.
Unless you really want to use mockito, you can quite easily handcraft your own test double.
What you can do is create an interface that knows how to send mail:
public interface TransportInterface {
public void send(Message msg);
}
Let the mail sending class use this interface to send the mail:
public class MailSender {
private TransportInterface transport;
public MailSender(TransportInterface transport) {
this.transport = transport;
}
public void sendMail(Message msg) {
/* This is the method from your question */
this.transport.send(msg);
}
}
In production you use an implemenation of TransportInterface that actually send the mail:
public class TransportAdapter implements TransportInterface {
private Transport transport; // Created elsewhere...
public void sendMail(Message msg) {
transport.send(msg);
}
}
And in your test code you can use a fake:
public class TransportFake implements TransportInterface {
public void sendMail(Message msg) {
// I don't send the mail!
}
}
(It's been a while since I coded java. Hope there are not too many errors. Also, you can probably do a better job naming the classes than I have.)
You can try to use Mockito library:
here is example code:
import org.mockito.Mock;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class YourTestClass{
#Mock
Transport t;
#Test
public void someTest(){
t = Mockito.mock(Transport .class);
Mockito.when(t.send()).thenReturn(true);
}
}
Here is a solution which works regardless of how the Transport t object is obtained:
#Test
public sendMail(
// Mocks all current and future Transport instances,
// including those belonging to subclasses (if any).
#Capturing final Transport t)
{
new SUT().sendMail();
// Verifies that the t.send() method was called:
new Verifications() {{ t.send(); }};
}
The mocking API used above is JMockit (which I develop).