Mocking Spring MVC BindingResult when using annotations - unit-testing

I'm migrating a Spring MVC controller to use the newer style annotations, and want to unit test a controller method that validates a command object (see simple example below).
#RequestMapping(method = RequestMethod.POST)
public String doThing(Command command, BindingResult result,
HttpServletRequest request, HttpServletResponse response,
Map<String, Object> model){
ThingValidator validator = new ThingValidator();
validator.validate(command, result);
... other logic here
}
My problem is I have to call the controller's method in my unit test, and provide mock values to satisfy its signature to exercise the code properly, and I cannot work out how to mock a BindingResult.
In the old style Controller the signature simply took a HttpServletRequest and HttpServletResponse, which were easily mockable, but due to the flexibility of the new annotation style, one has to pass a lot more in via the signature.
How can one mock a Spring BindingResult for use in a unit test??

You could also use something like Mockito to create a mock of the BindingResult and pass that to your controller method, ie
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verifyZeroInteractions;
#Test
public void createDoesNotCreateAnythingWhenTheBindingResultHasErrors() {
// Given
SomeDomainDTO dto = new SomeDomainDTO();
ModelAndView mv = new ModelAndView();
BindingResult result = mock(BindingResult.class);
when(result.hasErrors()).thenReturn(true);
// When
controller.create(dto, result, mv);
// Then
verifyZeroInteractions(lockAccessor);
}
This can give you more flexibility and simplify scaffolding.

BindingResult is an interface so can you not simply pass in one of Springs implementations of that interface?
I don't use annotations in my Spring MVC code but when I want to test the validate method of a validator I just pass in an instance of BindException and then use the values it returns in assertEquals etc.

As mentioned in this answer
A BindingResult is created for you by Spring MVC for each incoming HTTP request.
Thus, you don't want to mock the BindingResult.
Instead of calling the methods directly you should either do a real http call to the controller or perform a simulation using MockMvc.perform. Here is a tutorial on the official documentation that shows how to do it.

Related

how to do a dependancy injection with a method in micronaut for unit testing

I have a controller class:
#Controller("/x")
class Controller {
#Get
fun index(): String {
return "test test"
}
#Post
fun dopost (userid) = executelogin(login.userId)
}
This controller depends on the executelogin method down below:
fun executelogin(userid) =
do things
If I want to write a unit test, how would I go about using a dependancy injection to introduce a version of the executelogin method. Below is what I have so far and I am not sure if I am doing it correctly:
#MicronautTest
class Controllertest(val method: executelogin()) {
#Inject
lateinit var client: RxHttpClient
#Test
fun testController(){
val request: HttpRequest<String> = HttpRequest.POST("/hello","test")
val body: String = client.toBlocking().retrieve(request)
assertNotNull(body)
assertEquals(method,method)
}
}
Above in the unit test, I am making the executelogin method a paramter for the controllertest class, which I believe is called a method injection. I was wondering if this was the correct way to go about this or if adjustments need to be made. I apologise if some of the code does not quite make sense, I am new to micronaut and the concept of dependancy injections.
If I want to write a unit test, how would I go about using a
dependancy injection to introduce a version of the executelogin
method.
You can't go about using dependency injection to introduce a version fo the executelogin method, at least not directly you can't. Micronaut doesn't support method injection.
This isn't what you asked but just as an FYI... A common thing to do would be to inject some object that contains the method that you want to call.

Unit testing method with async

I am writing a unit test for a method using Moq framework.
The method calls the method Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) of System.Net.Http with await.
When I execute my test case the method does not return anything. Certainly, I am doing something wrong. Please help me to find it.
Please note that this call is not loosely coupled.
I did check the other answers like Unit testing async method - test never completes which have it as loosely coupled and could be mocked.
Edit 1:
The method similar to which I am trying to unit test
class Class1
{
public async Task<string> Method(string url)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,url);
HttpResponseMessage response = await new HttpClient().SendAsync(request);
return System.Convert.ToString( response);
}
}
Are you trying to mock the HttpClient? If so this isn't the recommended approach; instead of mocking HttpClient instantiate a real HttpClient, passing a mocked HttpMessageHandler into the constructor.
Source: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
Edit: to read the contents of a HTTP response you need to call the response.Content.ReadAsStringAsync() method instead of .ToString().

aem-mocks property test a servlet

Trying to write some proper AEM integration tests using the aem-mocks framework. The goal is to try and test a servlet by calling its path,
E.g. an AEM servlet
#SlingServlet(
paths = {"/bin/utils/emailSignUp"},
methods = {"POST"},
selectors = {"form"}
)
public class EmailSignUpFormServlet extends SlingAllMethodsServlet {
#Reference
SubmissionAgent submissionAgent;
#Reference
XSSFilter xssFilter;
public EmailSignUpFormServlet(){
}
public EmailSignUpFormServlet(SubmissionAgent submissionAgent, XSSFilter xssFilter) {
this.submissionAgent = submissionAgent;
this.xssFilter = xssFilter;
}
#Override
public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
}
}
Here is the corresponding test to try and do the integration testing. Notice how I've called the servlet's 'doPost' method, instead of 'POST'ing via some API.
public class EmailSignUpFormServletTest {
#Rule
public final AemContext context = new AemContext();
#Mock
SubmissionAgent submissionAgent;
#Mock
XSSFilter xssFilter;
private EmailSignUpFormServlet emailSignUpFormServlet;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
Map<String,String> report = new HashMap<>();
report.put("statusCode","302");
when(submissionAgent.saveForm(any(String.class)).thenReturn(report);
}
#Test
public void emailSignUpFormDoesNotRequireRecaptchaChallenge() throws IOException {
// Setup test email value
context.request().setQueryString("email=test.only#mail.com");
//===================================================================
/*
* WHAT I END UP DOING:
*/
// instantiate a new class of the servlet
emailSignUpFormServlet = new EmailSignUpFormServlet(submissionAgent, xssFilter);
// call the post method (Simulate the POST call)
emailSignUpFormServlet.doPost(context.request(),context.response());
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
// assert response is internally redirected, hence expected status is a 302
assertEquals(302,context.response().getStatus());
}
}
I've done a lot of research on how this could be done (here) and (here), and these links show a lot about how you can set various parameters for context.request() object. However, they just don't show how to finally execute the 'post' call.
What you are trying to do is mix a UT with IT so this won't be easy at least with the aem-mocks framework. Let me explain why.
Assuming that you are able to call your required code
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
Your test will end up executing all the logic in SlingAllMethodsServlet class and its parent classes. I am assuming that this is not what you want to test as these classes are not part of your logic and they already have other UT/IT (under respective Apache projects) to cater for testing requirements.
Also, looking at your code, bulk of your core logic resides in following snipper
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
Your UT criteria is already met by the following line of your code:
emailSignUpFormServlet.doPost(context.request(),context.response());
as it covers most of that logic.
Now, if you are looking for proper IT for posting the parameters and parsing them all the way down to doPost method then aem-mocks is not the framework for that because it does not provide it in a simple way.
You can, in theory, mock all the layers from resource resolver, resource provider and sling servlet executors to pass the parameters all the way to your core logic. This can work but it won't benefit your cause because:
Most of the code is already tested via other UT
Too many internal mocking dependencies might make the tests flaky or version dependant.
If you really want to do pure IT, then it will be easier to host the servlet in an instance and access it via HttpClient. This will ensure that all the layers are hit. A lot of tests are done this way but it feels a bit heavy handed for the functionality you want to test and there are better ways of doing it.
Also the reason why context.request().POST doesn't exist is because context.request() for is a mocked state for the sake of testing. You want to actually bind and mock Http.Post operations which needs some way to resolve to your servlet and that is not supported by the framework.
Hope this helps.

How to mock only the authorize() method in Form Request on Laravel 5.1?

We are implementing resource ownership and/or input validations through the use of FormRequest. Our controller tests are functional test so we don't mock Request but use the $this->call($method, $url, $params, ...).
The issue is that FormRequest is a subclass of Request. Is there a way to mock only the authorize() method but keeping the rest of the Request object non-mocked?
If so, how to? I already tried partial mocking with Mockery and it either didn't work or I did it wrong.
$this->mock(\Namespace\Http\Requests\CustomRequest::class . '[authorize]')
->shouldReceive('authorize')
-> .......
Use phpunit mocking system instead of Mockery.
$request = $this->getMock(\Namespace\http\Requests\CustomRequest::class, ['authorize']);
$request->expects($this->once())->method('authorize')->willReturn(true);
hope it helps;

Mock UserContext and FacesContext - jUnit

I'm trying to write some basic backingBean tests but I'm stuck with mocking the UserContext and facesContext.
This code is in the code that I'm trying to test:
UserContext uc = ContextProvider.getContext();
Locale locale = uc.getLocale();
ResourceBundle bundle = ResourceBundle.getBundle("AppMessages", locale);
String message = bundle.getString("this.is.the.message.key");
In another block of code I've got the following:
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().redirect(handleRedirect("someString"));
How could I mock these in a standard jUnit test using only mockito? Or do I have to use something like PowerMock?
Mockito can't mock static methods. You have a few options though:
Extract the code under test to methods which takes the UserContext and ResourceBundle or FacesContext instances as arguments
Wrap the static method calls in a factory object, and pass the factory objact instance as an argument to the code under test
PowerMock is an option, but slows down test execution and in my opinion allows bad-practice solutions
Instead create mocks for yourself, you can use Apache MyFaces Test, which provided already prepared Mock Objects for JSF artifacts. It will work better in a more wide range of cases, with less effort.