I'm fairly new to Mockito, and figured I would try to use it to test a SOAP Handler. However, this is turning out to be a much more painful than I would have expected/desired.
I'm looking to validate that my handler is able to extract the messageID in the header of a SOAPMessage. However, from the handler, the only way to get to the header is via the context/message/part/envelope/header. Using Mockito my solution was to mock my SOAPMessage, meant creating each individual object, and stubbing the method.
I can only imagine that there is an easier/cleaner way of accomplishing this:
#RunWith(MockitoJUnitRunner.class)
public class UUIDHandlerTest {
#Mock private SOAPMessage message;
#Mock private SOAPEnvelope envelope;
#Mock private SOAPHeader header;
#Mock private SOAPPart part;
#Mock
private SOAPMessageContext context;
#Before
public void setup() throws SOAPException{
when( context.getMessage()).thenReturn(message);
when( message.getSOAPPart()).thenReturn(part);
when( part.getEnvelope()).thenReturn(envelope);
when( envelope.getHeader()).thenReturn(header);
}
#Test
public void testHandleInboundMessage() {
when( context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).thenReturn(false);
when(header.getElementsByTagName(anyString())).thenAnswer(new Answer<NodeList>() {
/* (non-Javadoc)
* #see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock)
*/
#Override
public NodeList answer(InvocationOnMock invocation) throws Throwable {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(new String("<wsa:MessageID>messageId</wsa:MessageID>").getBytes()));
// TODO Auto-generated method stub
return doc.getElementsByTagName("wsa:MessageID");
}
});
// call the test class
new UUIDHandler().handleMessage(context);
// check the MDC value
assertEquals("messageId", MDC.get(LoggerConstants.DC_PROPERTY_MESSAGE_ID));
}
}
Like I said, it works, but it looks like a very ugly/heavy weight solution.
Is there anyway to do this easier/cleaner?
Thanks!
Eric
SOAPMessageContext context =
mock(SOAPMessageContext.class, RETURNS_DEEP_STUBS);
when(context.getMessage().getSOAPPart().getEnvelope().
getHeader().getElementsByTagName(anyString())).
then(...);
Please also pay attention to the notes on using deep stubs in the mockito documentation.
http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#RETURNS_DEEP_STUBS
Annotation style:
#Mock(answer = Answers.RETURNS_DEEP_STUBS) SOAPMessageContext context;
A bit late here, but I prefer spawning a working Endpoint which proxies a mockito mock. This lets me test the whole stack, including interceptors and/or handlers, which should be helpful for your use-case.
I've put up a simple JUnit Rule which simplifies things somewhat here. The resulting test-cases should be small and clean. I recommend loading test XML responses directly from XML files, because that is faster and more simple to maintain.
Don't mock things like this.
Listen to the code... its telling you that this is not the right way to do it.
Rather, just create a (real) message that has some known data in it, and assert that your code does the right stuff with it.
e.g.
MessageIdExtractor extractor = new MessageIdExtractor(); // <- class you are testing
String expectedMessageId = "xxxxxx";
Message m = new SOAPMessage( ).setMessageId(expectedMessageId);
assertThat(extractor.extractIdFrom(m), equalTo(expectedMessageId));
Related
I'm trying to mock the return value for a method using the when call from mockito. However, I'm new to this and I may perhaps be misunderstanding how mockito works, since the call is failing inside the method mocked when that calls another method. I thought regardless of how that method is implemented, I should be getting the return value I'm asking for? Or do I need to mock also the internals for that method? I feel that shouldn't be it.
public boolean verifyState(HttpServletRequest request, String s) {
String stateToken = getCookieByName(request, STATE_TOKEN);
String authToken = getCookieByName(request, AUTHN);
boolean isValidState = true;
if (isValidState) {
try {
log.info(getEdUserId(stateToken, authToken));
return true;
} catch (Exception e) {
ExceptionLogger.logDetailedError("CookieSessionUtils.verifyState", e);
return false;
}
} else {
return false;
}
}
public String getEdUserId(String stateToken, String authToken) throws Exception {
String edUserId;
Map<String, Object> jwtClaims;
jwtClaims = StateUtils.checkJWT(stateToken, this.stateSharedSecret); // Failing here not generating a proper jwt token
log.info("State Claims: " + jwtClaims);
edUserId = sifAuthorizationService.getEdUserIdFromAuthJWT(authToken);
return edUserId;
}
My test:
#ActiveProfiles(resolver = MyActiveProfileResolver.class)
#WebMvcTest(value = CookieSessionUtils.class, includeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ApiOriginFilter.class, ValidationFilter.class})})
class CookieSessionUtilsTest {
#Autowired
private CookieSessionUtils cookieSessionUtils; // Service class
#Mock
private CookieSessionUtils cookieSessionUtilsMocked; // Both the method under test and the one mocked are under the same class, so trying these two annotations together.
#Mock
private HttpServletRequest request;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testVerifyState1() throws Exception {
//...Some mocks for getCookieName
UUID uuid = UUID.randomUUID();
when(cookieSessionUtils.getEdUserId(anyString(), anyString()).thenReturn(eq(String.valueOf(uuid))); // When this line runs it fails on verifyState method
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
UPDATE
Attempt using anyString() instead of eq().
Thank you.
Your test is broken in a few places.
Setting expectations on a real object
You should call Mockito.when on mocks and spies, not on System under test. Mockito normally reports it with a clear error message, but you throw a NPE from getEdUserId, so this is reported instead. The NPE stems from the fact that both eq and anyString return null, which is passed to the real method.
Invalid use of matchers
As #StefanD explained in his answer eq("anyString()") is not matching any string. It matches only one string "anyString()"
Returning a mather instead of real object
thenReturn(eq(String.valueOf(uuid)))
This is illegal position for a matcher.
Mixing Mockito and Spring annotations in a WebMvcTest
This is a common error. Mockito does not inject beans to the spring context.
From the code provided it is unclear what CookieSessionUtils is (Controller? ControllerAdvice?) and what is the correct way to test it.
Update
It seems that you are trying to replace some methods under test. A way to do it is to use a Spy.
See https://towardsdatascience.com/mocking-a-method-in-the-same-test-class-using-mockito-b8f997916109
The test written in this style:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#Spy
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
doReturn(String.valueOf(uuid)).when(cookieSessionUtils).getEdUserId(anyString(), anyString());
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
An alternative way is to call the real method, but to mock all collaborators: StateUtils and sifAuthorizationService. I would probably go with this one, if you want to test public getEdUserId.
Test written when mocking collaborators:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
when(sifAuthorizationService.getEdUserIdFromAuthJWT(cookie2.getValue())).thenReturn(String.valueOf(uuid));
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
I took the assumption that StateUtils.checkJWT does not need to be mocked
The points above are still valid and need to be resolved in either case.
Remarks
As the system under test is currently a Service, I suggest to drop WebMvcTest and test it with plain mockito instead.
Should SUT be a service? It is more typical to handle auth code in filters.
note usage of doReturn when stubbing a method on a spy.
You use mocks in more places than needed. For example Cookie is trivial to construct, there is no point in using a mock
The error is here:
when(cookieSessionUtils.getEdUserId(eq("anyString()"), eq("anyString()"))).thenReturn(eq(String.valueOf(uuid)));
It should read like
when(cookieSessionUtils.getEdUserId(anyString()), anyString()).thenReturn(uuid);
Please refer to the Mockito documentation of Argument matchers.
Because the argument matchers looking for the string "anyString()" they never match the actual parameters the method call is providing and so there is never returned the uuid you expecting.
I have the following bit of code that some long-departed developer wrote:
Document doc = new Document();
dao.load(doc, documentId);
This bit of code is very close to untestable, as I don't know how to mock out the result.
Is it possible to use an ArgumentCaptor to mock the result of the dao to provide a given value for the doc value for a given documentId? I'd prefer NOT to change the dao call at this time (if possible).
Edit: I was requested to flesh out the above example. This is a simplified version of it:
#Controller
public class DocumentController {
#Autowired
private DocumentDAO dao;
public Document getDocument(int documentId) throws Exception {
// ensure documentId is valid
// ensure user has access to documentId
Document doc = new Document();
int rc = dao.load(doc, documentId);
// if rc is bad throw an Exception
return doc;
}
I understand it would make MUCH more sense to return the Document from the load() method, unfortunately the original developer was trying to do two things at once.
I'm trying to NOT introduce new methods if possible. I just need to know if I'm using Mockito to mock a DocumentDAO instance, can I adjust an input in the manner the above method does.
Not sure if I understood your question, but maybe you are looking for something of the sort:
public class DocumentControllerTest {
#Mock
private DocumentDAO dao;
#InjectMocks
private DocumentController controller;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void simpleTest() {
Document inputDoc = ...
int yourid = ...
Mockito.when(dao.load(inputDoc,yourid)).thenReturn(whateveryouwanttoreturn);
Document d = controller.getDocument(inputDoc,yourid);
//check if dao method was called with the expected input
Mockito.verify(dao, Mockito.times(1)).load(inputDoc,yourid);
// do yur asserts
Assert.assertNotNull("Doc shouldn't be null", d);
Assert.assertEquals("Doc shoud be the expected one.", inputDoc, d);
}
}
I'm writing a Dropwizard application and using Feign for building client calls to outside services. I have custom encoders and decoders I'm registering with the feign.Builder like so:
this.feignBuilder = Feign.builder()
.contract(new JAXRSContract()) // we want JAX-RS annotations
.encoder(new JacksonEncoder()) // same as what dropwizard is using
.decoder(new CustomDecoder())
.errorDecoder(new CustomErrorDecoder())
.requestInterceptor(new AuthKeyInterceptor(config.getInterceptor()));
I'm writing unit tests for the feign client calls so I can watch how the feign machinery deals with my encoder/decoder overrides and bubbles on exceptions. I'm not interested in writing integration tests with a fake server right now (this is the most common type of test i see people writing for this situation).
This should be straight forward. I want to mock the point at which feign makes the request and have it return my fake response. That means I should mock the call to feign.Client.Default.execute so it returns my fake response when it makes the request a this call site. An example of what that mock looks like:
String responseMessage = "{\"error\":\"bad\",\"desc\":\"blah\"}";
feign.Response feignResponse = FeignFakeResponseHelper.createFakeResponse(404,"Bad Request",responseMessage);
Client.Default mockFeignClient = mock(Client.Default.class);
try {
when(mockFeignClient.execute(any(feign.Request.class),any(Request.Options.class))).thenReturn(feignResponse);
} catch (IOException e) {
assertThat(true).isFalse(); // fail nicely
}
No luck. The Cleint.Default class isn't mocked when I reach the call site for the request in the code. What am I doing wrong?
As mentioned before, Mockito is not powerful enough.
I solved this with a manual mock.
It's easier than it sounds:
MyService.Java
public class MyService{
//My service stuff
private MyFeignClient myFeignClient;
#Inject //this will work only with constructor injection
public MyService(MyFeignClient myFeignClient){
this.MyFeignClient = myFeignClient
}
public void myMethod(){
myFeignClient.remoteMethod(); // We want to mock this method
}
}
MyFeignClient.Java
#FeignClient("target-service")
public interface MyFeignClient{
#RequestMapping(value = "/test" method = RequestMethod.GET)
public void remotemethod();
}
If you want to test the code above while mocking the feignclient, do this:
MyFeignClientMock.java
#Component
public class MyFeignClientMock implements MyFeignClient {
public void remoteMethod(){
System.out.println("Mocked remoteMethod() succesfuly");
}
}
MyServiceTest.java
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
private MyService myService;
#Inject
private MyFeignClientMock myFeignClientMock;
#Before
public void setUp(){
this.myService = new MyService(myFeignClientMock); //inject the mock
}
//Do tests normally here...
}
It turns out Mockito is not powerful enough to do the thing I assumed it could do. The correct solution is to use PowerMockito to mock the constructor so Client.Default returns the mocked instance when it is instantiated in the class that holds that reference.
After a lot of compilation-error pain I got PowerMockito to compile and it seemed like it was going to work. Alas it failed to return my mock and the calls were still going through. I've tried PowerMockito in the past and never got around to using it because of the extra problems it caused. So I'm still of the opinion that it's not super easy to just plug and play.
It's a shame that trying to do something like this is so hard.
I have some problem with wirint testcase for my JSF app. So i want to test my logout method:
FacesContext context = EasyMock.createMock(FacesContext.class);
String userName = "testUserName";
HttpSession session = EasyMock.createMock(HttpSession.class);
ExternalContext ext = EasyMock.createMock(ExternalContext.class);
EasyMock.expect(ext.getSession(true)).andReturn(session);
EasyMock.expect(context.getExternalContext()).andReturn(ext).times(2);
context.getExternalContext().invalidateSession();
EasyMock.expectLastCall().once();
EasyMock.replay(context);
EasyMock.replay(ext);
EasyMock.replay(session);
loginForm = new LoginForm();
loginForm.setUserName(userName);
String expected = "login";
String actual = loginForm.logout();
context.release();
Assert.assertEquals(expected, actual);
EasyMock.verify(context);
EasyMock.verify(ext);
EasyMock.verify(session);
My logout method is:
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/authentication/login.xhtml?faces-redirect=true";
}
My problem is that i got a nullpointer exception here:
EasyMock.expectLastCall().once()
How should it be properly tested? I guess it is something with the mocks but i couldnt find a solution, how could i mock properly the FacesContext in this case
In order to make the above work you could use for example PowerMock which is a framework that allows you to extend mock libraries like EasyMock with extra capabilities. In this case it allows you to mock the static methods of FacesContext.
If you are using Maven, use following link to check the needed dependency setup.
Annotate your JUnit test class using these two annotations. The first annotation tells JUnit to run the test using PowerMockRunner. The second annotation tells PowerMock to prepare to mock the FacesContext class.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ FacesContext.class })
public class LoginFormTest {
Now go ahead and mock FacesContext using PowerMock like you would do for the other classes. Only difference is that this time you perform replay() and verify() on the class not the instance.
#Test
public void testLogout() {
// mock all static methods of FacesContext
PowerMock.mockStatic(FacesContext.class);
FacesContext context = EasyMock.createMock(FacesContext.class);
ExternalContext ext = EasyMock.createMock(ExternalContext.class);
EasyMock.expect(FacesContext.getCurrentInstance()).andReturn(context);
EasyMock.expect(context.getExternalContext()).andReturn(ext);
ext.invalidateSession();
// expect the call to the invalidateSession() method
EasyMock.expectLastCall();
context.release();
// replay the class (not the instance)
PowerMock.replay(FacesContext.class);
EasyMock.replay(context);
EasyMock.replay(ext);
String userName = "testUserName";
LoginForm loginForm = new LoginForm();
loginForm.setUserName(userName);
String expected = "/authentication/login.xhtml?faces-redirect=true";
String actual = loginForm.logout();
context.release();
Assert.assertEquals(expected, actual);
// verify the class (not the instance)
PowerMock.verify(FacesContext.class);
EasyMock.verify(context);
EasyMock.verify(ext);
}
I've created a blog post which explains the above code sample in more detail.
This:
FacesContext context = EasyMock.createMock(FacesContext.class);
does not change the return value from this (in the class being tested):
FacesContext.getCurrentInstance()
You need to extend FacesContext then call the protected method setCurrentInstance with your mocked FacesContext.
This is the class I'm trying to test (it calculates the size of HTTP page):
import javax.ws.rs.core.MediaType;
import com.sun.jersey.api.client.*;
public class Loader {
private Client client;
public Loader(Client c) {
this.client = c;
}
public Integer getLength(URI uri) throws Exception {
return c.resource(uri) // returns WebResource
.accept(MediaType.APPLICATION_XML) // returns WebResource.Builder
.get(String.class) // returns String
.length();
}
}
Of course it just an example, not a real-life solution. Now I'm trying to test this class:
public class LoaderTest {
#Test public void shouldCalculateLength() throws Exception {
String mockPage = "test page"; // length is 9
Client mockedClient = /* ??? */;
Loader mockedLoader = new Loader(mockedClient);
assertEquals(
mockPage.length(),
mockedLoader.getLength(new URI("http://example.com"))
);
}
}
How should I mock com.sun.jersey.api.client.Client class? I'm trying to use Mockito, but any other framework will be OK, since I'm a newbie here..
Not really related to your question, but may come in handy later, is the Jersey Test Framework. Check out these blog entries by one of the Jersey contributors;
http://blogs.oracle.com/naresh/entry/jersey_test_framework_makes_it
http://blogs.oracle.com/naresh/entry/jersey_test_framework_re_visited
Back on topic, to test your Loader class you can simply instantiate it with a Client obtained from Client.create(). If you are using Maven you can create a dummy test endpoint (in src/test/java) to call and the Jersey Test framework will load it in Jetty.
You example is really complex, i wasnt able to run it with newest version of jersey, so i created those classes and here is how i mock it with EasyMock.
String mockPage = "test page"; // length is 9
RequestBuilder requestBuilderMock = createNiceControl().createMock(RequestBuilder.class);
expect(requestBuilderMock.get((Class < String >) anyObject())).andReturn("12345678").anyTimes();
replay(requestBuilderMock);
WebResource webResourcemock = createNiceControl().createMock(WebResource.class);
expect(webResourcemock.accept((String[]) anyObject())).andReturn(requestBuilderMock).anyTimes();
replay(webResourcemock);
Client clientMock = createNiceControl().createMock(Client.class);
expect(clientMock.resource((URI) anyObject())).andReturn(webResourcemock).anyTimes();
replay(clientMock);
Loader mockedLoader = new Loader(clientMock);
assertEquals((Integer) mockPage.length(), mockedLoader.getLength(new URI("http://example.com")));
If any of classes that you are trying to mock doesnt have default constructor then you should use
http://easymock.org/api/easymock/3.0/org/easymock/IMockBuilder.html#withConstructor%28java.lang.Class...%29