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.
Related
i am using spring cloud's eureka and feign to communicate between some services (lets say A and B). Now id like to unittest my service layer of a single service (A). The problem is, that this service (A) is using a feign client to request some information of the other service (B).
Running the unittests without any special configuration throws the following exception: java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-b => but i do not want any server to run.
My question is: Is there a way to mock the feign client, so i can unittest my service (A) without running an eureka instance and service (B)?
Edit:
I ended up creating a stub for the feign client. The stub is marked as a primary component to force spring instantiating the stub within my tests.
This is the solution i came up with.
//the feign client
#FeignClient("user")
public interface UserClient {
UserEntity getUser();
}
//the implementation i use for the tests
#Component
#Primary //mark as primary implementation
public class UserClientTestImpl implements UserClient {
#Override public UserEntity getUser() {
return someKindOfUser;
}
}
The question is ... do you even need to mock? I often see that people mention "mock" as the first solution to anything that "should not be part of the unit test". Mocking is a technique, not the solution to everything. (see here).
If you are still at the early stages of your code, just refactor and use something else instead of depending on the concrete instance of the Feign Client. You might use an interface, an abstract class, a trait or whatever you want. Don't depend on the object itself, otherwise you have to "mock it".
public interface IWebClient {
public String get(...);
public String post(...);
}
To the question: but I will have other code that will do exactly the same (except that it will be on the concrete instance of Feign), what do I do then?
Well, you can write a functional test and call an instance of a web server that you can setup locally - or use Wiremock, as mentioned by Marcin Grzejszczak in one of the answers.
public class FeignClientWrapper implements IWebClient {
private feign = something
public String get() {
feign.get( ... )
}
public String post() {
feign.post( ... )
}
}
Unit tests are used to test algorithms, if/else, loops: how units work. Don't write code to make mocks fit - it must be the other way around: your code should have less dependencies, and you should mock only when you need to verify the behavior (otherwise you can use a stub or a fake object): do you need to verify the behavior? Do you need to test that a particular method gets called in your code? Or that a particular method gets called with X, Y, and Z for 3 times in a row? Well, then yes, mocking is ok.
Otherwise, use a fake object: what you want is to test just the call/response and maybe the status code. All you probably want is to test how your code reacts to different outputs (e.g., the field "error" is present or not in a JSON response), different status codes (assuming that the Client documentation is right: 200 OK when GET, 201 when POST, etc).
Mocking a feign client is really useful in microservice component tests. You want to test one microservice without having to start all the other microservices.
If you're using Spring (and it looks like you are), the #MockBean annotation together with a bit of Mockito code will do the job.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestYourComponent {
#Configuration
#Import({YourConfiguration.class})
public static class TestConfiguration {
}
#MockBean
private UserClient userClient;
#Test
public void someTest()
{
//...
mockSomeBehavior();
//...
}
private void mockSomeBehavior() {
Mockito.doReturn(someKindOfUser).when(userClient).getUser();
}
}
If you need to use a mock you can use Wiremock to stub the response for a given request - http://wiremock.org/stubbing.html. That way you will do integration tests with real HTTP requests sent. For unit testing the answer from #Markon is very good.
I have inherited some code that isn't tested and which loads a resource using a method like :
SomeClass.class.getClassLoader().getResource("somefile");
I've written the following test but there are 0 interactions with the Mock class loader I've created. Can anyone comment on whether this type of test is possible.
public enum SomeClass {
INSTANCE;
public boolean someMethod() {
URL pathToLicense = SomeClass.class.getClassLoader().getResource("somefile");
return false;
}
}
#Test
public void testLicenseWorkflow(){
ClassLoader cl = PowerMockito.mock(ClassLoader.class);
File f = new File("someFile");
assertTrue(f.exists());
logger.info(f.getCanonicalPath() );
when(cl.getResource("somefile")).thenReturn(f.toURL());
verify(cl).getResource("somefile");
assertTrue(SomeClass.INSTANCE.someMethod());
}
Update - Adding a resources via Classloader
I've also tried the following but the someMethod this doens't seem to work either
new URLClassLoader(((URLClassLoader) SomeClass.INSTANCE.getClass().getClassLoader()).getURLs()) {
#Override
public void addURL(URL url) {
super.addURL(url);
logger.info("Calling add URL");
}
}.addURL(f.toURI().toURL());
You are not passing cl to anything. You prepare a mock for a classloader but then proceed to load the resource with another classloader, the one that loaded SomeClass. That is why you have 0 interactions in your mock.
And about your first question, it is possible if somehow you pass your mocked classloader to the method that actually loads the resource. For example, something like
public boolean someMethod(Classloader loader) {
URL pathToLicense = loader.getResource("somefile");
return false;
}
But I have to say that IMO, this test is not very useful, you should be mocking your own components, not java classes. If your goal mocking the classloader is to inject a different file when testing, a better approach is to change your code to receive a Stream and inject a stream connected to the file in production and in testing inject a stream connected to an element in memory.
In other words, resources make for bad testing when they need to be changed at test time
I'm about to delve into test in the PHP world and I have some questions. I have a controller which handles a loan application. The bulk of the work is then delegated to a ProcessLoanApplication class.
ApplyController
class ApplyController extends Controller
{
public function indexAction(Request $request)
{
$form = $this->createForm(new LoanApplication());
if($request->getMethod() == 'POST') {
$form->bind($request);
if($form->isValid()) {
$session = $this->getRequest()->getSession();
$loan_app_processor = new Tasks\ProcessLoanApplication($form, $session);
$loan_app_processor->process();
return $this->redirect($this->generateUrl('apply_thanks'));
}
}
Tasks\ProcessLoanApplication
class ProcessLoanApplication
{
private $_QuickBaseModels;
private $_session;
private $_app; // submitted form data
private $_existingApp = false; // holds existing application in QB, if it exists
public function __construct(Form $form, Session $session)
{
$this->_app = $form->getNormData();
$this->_session = $session;
// save the form data to a session
$session->set('application', $this->_app);
// create the quickbase table models objects
$this->_QuickBaseModels['GenFnHome\Application'] = new GenFnHome\Application();
$this->_QuickBaseModels['GenFnHome\SSN'] = new GenFnHome\SSN();
}
public function process()
{
$this->_existingApp = $this->_getExistingApplication();
$app_status = $this->_existingApp[GenFnHome\SSN::LogInApplicationStatus];
if(!$this->_existingApp || ($this->_existingApp && ($app_status !== 'PENDING' && $app_status !== 'OPEN' && $app_status !== 'EXPIRED')))
return $this->_saveNewLoanApplication();
if($app_status == 'EXPIRED') $this->_reOpenApplication();
}
There's a lot going on here, so I will outline it first:
User makes a requests for the application
Application form is validated
If valid, process loan application
Check if the user already has an app, if so - do X, if not Y
The application is persisted in an 'online database' (QuickBase) that my application communicates with via XML over HTTP (in other words, there is no real db)
My questions to the community:
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
Anything else I should know?
Thanks!
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
I recommend to test every class you build. If you are using Test Driven Development, the test declares what you are building, no test no code.
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Every Unit test should be isolated and should only test the Class that you are testing. You should use Mock object (use the PHPunit mock library or other 3th party libraries as Mockery) if one object dependences on another object.
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
You can use the Symfony's WebTestCase that provides easy methods to imitate a browser request, learn more about that in the documentation. We call that Functional Testing.
This is usually the stage after Unit Testing. In Unit Testing you will test each individual class (it's a good practice to unit test your controller) and after that you write your Functional Tests which combines everything and tests if it works like expected.
for Controllers you should use functional tests (http://symfony.com/doc/2.0/book/testing.html#functional-tests). With them you can emulate browser and user's actions like submiting form and checking validation, database changes, http status codes and so on.
You should not forget to unit test ProcessLoanApplication.
I dont realy know why you pass form object to ProcessLoanApplication anyway. You should pass entity - it has normdata already.
First up, where my knowledge is at:
Unit Tests are those which test a small piece of code (single methods, mostly).
Integration Tests are those which test the interaction between multiple areas of code (which hopefully already have their own Unit Tests). Sometimes, parts of the code under test requires other code to act in a particular way. This is where Mocks & Stubs come in. So, we mock/stub out a part of the code to perform very specifically. This allows our Integration Test to run predictably without side effects.
All tests should be able to be run stand-alone without data sharing. If data sharing is necessary, this is a sign the system isn't decoupled enough.
Next up, the situation I am facing:
When interacting with an external API (specifically, a RESTful API that will modify live data with a POST request), I understand we can (should?) mock out the interaction with that API (more eloquently stated in this answer) for an Integration Test. I also understand we can Unit Test the individual components of interacting with that API (constructing the request, parsing the result, throwing errors, etc). What I don't get is how to actually go about this.
So, finally: My question(s).
How do I test my interaction with an external API that has side effects?
A perfect example is Google's Content API for shopping. To be able to perform the task at hand, it requires a decent amount of prep work, then performing the actual request, then analysing the return value. Some of this is without any 'sandbox' environment.
The code to do this generally has quite a few layers of abstraction, something like:
<?php
class Request
{
public function setUrl(..){ /* ... */ }
public function setData(..){ /* ... */ }
public function setHeaders(..){ /* ... */ }
public function execute(..){
// Do some CURL request or some-such
}
public function wasSuccessful(){
// some test to see if the CURL request was successful
}
}
class GoogleAPIRequest
{
private $request;
abstract protected function getUrl();
abstract protected function getData();
public function __construct() {
$this->request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Note: This is a PHP5 / PHPUnit example
Given that testTheRequest is the method called by the test suite, the example will execute a live request.
Now, this live request will (hopefully, provided everything went well) do a POST request that has the side effect of altering live data.
Is this acceptable? What alternatives do I have? I can't see a way to mock out the Request object for the test. And even if I did, it would mean setting up results / entry points for every possible code path that Google's API accepts (which in this case would have to be found by trial and error), but would allow me the use of fixtures.
A further extension is when certain requests rely on certain data being Live already. Using the Google Content API as an example again, to add a Data Feed to a Sub Account, the Sub Account must already exist.
One approach I can think of is the following steps;
In testCreateAccount
Create a sub-account
Assert the sub-account was created
Delete the sub-account
Have testCreateDataFeed depend on testCreateAccount not having any errors
In testCreateDataFeed, create a new account
Create the data feed
Assert the data feed was created
Delete the data feed
Delete the sub-account
This then raises the further question; how do I test the deletion of accounts / data feeds? testCreateDataFeed feels dirty to me - What if creating the data feed fails? The test fails, therefore the sub-account is never deleted... I can't test deletion without creation, so do I write another test (testDeleteAccount) that relies on testCreateAccount before creating then deleting an account of its own (since data shouldn't be shared between tests).
In Summary
How do I test interacting with an external API that effects live data?
How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?
What do I do when a test fails and the live data is left in an inconsistent state?
How in code do I actually go about doing all this?
Related:
How can mocking external services improve unit tests?
Writing unit tests for a REST-ful API
This is more an additional answer to the one already given:
Looking through your code, the class GoogleAPIRequest has a hard-encoded dependency of class Request. This prevents you from testing it independently from the request class, so you can't mock the request.
You need to make the request injectable, so you can change it to a mock while testing. That done, no real API HTTP requests are send, the live data is not changed and you can test much quicker.
I've recently had to update a library because the api it connects to was updated.
My knowledge isn't enough to explain in detail, but i learnt a great deal from looking at the code. https://github.com/gridiron-guru/FantasyDataAPI
You can submit a request as you would normally to the api and then save that response as a json file, you can then use that as a mock.
Have a look at the tests in this library which connects to an api using Guzzle.
It mocks responses from the api, there's a good deal of information in the docs on how the testing works it might give you an idea of how to go about it.
but basically you do a manual call to the api along with any parameters you need, and save the response as a json file.
When you write your test for the api call, send along the same parameters and get it to load in the mock rather than using the live api, you can then test the data in the mock you created contains the expected values.
My Updated version of the api in question can be found here.
Updated Repo
One of the ways to test out external APIs is as you mentioned, by creating a mock and working against that with the behavior hard coded as you have understood it.
Sometimes people refer to this type of testing as "contract based" testing, where you can write tests against the API based on the behavior you have observed and coded against, and when those tests start failing, the "contract is broken". If they are simple REST based tests using dummy data you can also provide them to the external provider to run so they can discover where/when they might be changing the API enough that it should be a new version or produce a warning about not being backwards compatible.
Ref: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-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.