How to unit test on controller level in Vert.x - unit-testing

UserController:
class UserController(private val graphRepository: GraphRepository) : Controller {
override fun installRoutes(router: Router) {
router.install {
post("/api/v1/user").handler(this#UserController::addUser)
}
}
}
Testing route and calling route handler "addUser":
#Test
fun newUserAdded() {
Mockito.`when`(mockRoutingContext.queryParam("id")).thenReturn(listOf("1"))
Mockito.`when`(mockGraphRepository.getUser("1")).thenReturn(Promise.ofSuccess(null))
Mockito.`when`(mockGraphRepository.enrollUser(any())).thenReturn(Promise.ofSuccess(Unit))
Mockito.`when`(mockRoutingContext.response()).thenReturn(mockHttpServerResponse)
Mockito.doNothing().`when`(mockHttpServerResponse).end()
UserController(mockGraphRepository).addUser(mockRoutingContext)
Mockito.verify(mockRoutingContext, Mockito.times(1)).response()
Mockito.verify(mockHttpServerResponse).end()
}
The main question is how to test the controller route without explicitly calling "addUser" on "UserController" because I want to make the controller function private.

Mocking behavior for types you don't own is generally discouraged for a variety of reasons, such as (but not limited to):
If the real implementation of the mocked dependency changes, the mock's behavior will not automatically reveal any forward-breaking changes.
The more mocks a test introduces, the more cognitive load the test carries, and some tests require a lot of mocks in order to work.
The approach that works best for me is to think of these more as integration tests, and avoid the mocks all together.
To achieve this, I've got an abstract VertxPlatform class that I extend that contains references to resources I commonly refer to across a variety of tests:
the Vertx instance itself
a Router
an EventBus
an HttpServer
a WebClient
These resources is reinitialized per invocation of each test, and the Router is subsequently associated with the HttpServer.
A typical test ends up looking something like this:
class MyHandlerIT : VertxPlatform() {
private lateinit var myHandler: MyHandler // <-- the component under test
#Before override fun setUp(context: TestContext) {
super.setUp(context) // <-- reinitializes all the underlying Vert.x components
myHandler = MyHandler()
router.post("/my/handler/path")
.handler(myHandler.validationHandler())
.handler(myHandler.requestHandler(vertx))
.failureHandler(myHandler.failureHandler())
}
#After override fun tearDown(context: TestContext) {
super.tearDown(context)
}
#Test fun status_400_on_some_condition(context: TestContext) {
val async = context.async()
testRequest(POST, path = "/my/handler/path", params = null, body = null, headers = null)
.subscribeBy(
onSuccess = { response ->
context.assertEquals(BAD_REQUEST.code(), response.statusCode())
async.complete()
},
onError = { error ->
context.fail(error)
}
)
}
}
In each individual test you might have some more case-specific setup. For example, if MyHandler gets results from your GraphRepository via the EventBus you could setup a fake Consumer within the scope of that test that replies with a pre-canned result that server back the values you were otherwise trying to mock.
Hope this helps, or at least inspires some thought!

Related

Spring boot Unit test with #WebMvcTest - execution doesn't enter service layer from controller and returns null without error

I am writing unit test for a controller of my spring boot application.
I have typical MVC classes: ObjectSchemaController, ObjectSchemaService and ObjectSchemaDao.
I have written unit test with #WebMvcTest and mocked my service and dao class with #MockBean. (following this guide: https://www.baeldung.com/spring-boot-testing)
Below is my unit test :
#RunWith(SpringRunner.class)
#WebMvcTest(ObjectSchemaController.class)
public class ObjectSchemaControllerTest2 {
#Autowired
private MockMvc mvc;
#MockBean
private ObjectSchemaService service;
#MockBean
private ObjectSchemaDao dao;
#Autowired
ObjectMapper objectMapper;
#Test
public void testCreateObjectSchemaPass() throws Exception {
String payload = "{\"some_key\":\"some val\"}";
ObjectSchema objectSchema = objectMapper.readValue(payload, ObjectSchema.class);
Mockito.when(service.createSchema(objectSchema))
.thenReturn(objectSchema);
Mockito.when(dao.createSchema(objectSchema)).thenReturn(objectSchema);
mvc.perform(MockMvcRequestBuilders.post("/objectservice/schema/")
.contentType("application/json")
.content(objectMapper.writeValueAsString(objectSchema)))
.andExpect(status().isOk());
}
}
below is my service class:
#Service
public class ObjectSchemaService {
#Autowired
ObjectSchemaDao objectSchemaDao;
public ObjectSchema createSchema(#Valid ObjectSchema objectSchema)throws Exception {
return objectSchemaDao.createSchema(objectSchema);
}
}
The issue I am facing with Unit test is, the service layer doesn't get executed and returns null value.
When I debug, I can see execution reaching in my controller class and ObjectSchemaService as being mockito-mocked in the controller. But the execution never goes in service layer and the value returned by service method is null.
I have referenced other guides- they are doing similar steps. But its not working for me. What am I missing here?
I have also seen this post with similar issue.
Unit Test POST with #WebMvcTest - #MockBean Service returns null
I made sure the input objects to both my actual controller and the one I am passing in unit case are instances of same class.
You are mocking the ObjectSchemaService but no behaviour is expected.
You need to setup the behaviour for the services that are mocked. So depending on the method signature and result somethink like.
Mockito.when(service.createSchema(Mockito.any(ObjectSchema.class)).thenReturn(objectSchema);
At the moment the ObjectSchemaService mock just returns a default value which is null in your case.
In order to be transparent and unobtrusive all Mockito mocks by default return 'nice' values. For example: zeros, falseys, empty collections or nulls.
If you update your answer with details for ObjectSchemaService I could also update my answer.
You mock ObjectSchemaService so you need to tell the service how mock the values from the service when a method is called. If you don't mock the values of the service Mockito don't know what they have to return always give you null. Not need to mock ObjectSchemaDao in this test.
Note: I use Lombok in the code as ObjectSchema.builder() to return the object with the Id when is stored in the database, you can use a constructor. Assuming the service return the object.
The code looks like this:
import static org.mockito.BDDMockito.given;
#Test
public void testCreateObjectSchemaPass() throws Exception {
String payload = "{\"some_key\":\"some val\"}";
ObjectSchema objectSchema = objectMapper.readValue(payload, ObjectSchema.class);
given(service.createSchema(objectSchema)).willReturn(
ObjectSchema.builder()
.id(1)
.someKey("Some Val")
.build());
mvc.perform(MockMvcRequestBuilders.post("/objectservice/schema/").contentType("application/json").content(objectMapper.writeValueAsString(objectSchema)))
.andExpect(status().isOk());
}

Unit testing not using correct mocking class

I am using Rhino.Mocks and Structure map to help unit test my code. I have several tests that pass when they are ran by themselves, but when ran as a group fail to pass. The setup code for these unit tests is:
[TestInitialize()]
public void Setup()
{
ObjectFactory.Initialize(x =>
{
x.For(IManager)().Use(Handler)();
});
}
In my tests, I stub out this interface and call the method.
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return(null);
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsFalse(areMultiple);
}
Test Method 2
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return("123");
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsTrue(areMultiple);
}
This is unit testing the following code.
public bool AreMultiple(WorkOrder workOrder)
{
string secondUser = _handler.GetTwoUserName(_workflowManager.GetNumberForProject(workOrder.Id));
if (String.IsNullOrEmpty(secondUser ))
{
return false;
}
return true;
}
When I run them by themselves, they work fine. When I run them together, the first passes and the second fails. When I debug the second one, I find that that the return value in the Stubbed method is still coming back as null. How do I get this to use the new Stubbed method.
UPDATE.
I am using StructureMap as my container. From what I have been able to find, the following code is what is used to dispose of the container I got it from this link. When I added this, the test still fail when ran together, but pass when ran individually.
[TestCleanup()]
public void TestCLeanup()
{
ObjectFactory.Container.Dispose();
}
The tests work one by one but fails if run all together. The problem should be in the common part which is being shared across the tests making them dependent from each other. In this particular case that is static ObjectFactory which is nothing else but a Service Locator (anti-pattern).
In the tests, you mock the IManager interface and register it in the ObjectFactory:
ObjectFactory.Inject(typeof(IManager), mackIManager);
Then the SUT uses the ObjectFactory service locator to resolve and use the mocked interface (_handler field):
string secondUser = _handler.GetTwoUserName(...)
I suspect the first test registers the _handler and never clean it up properly, so that the same instance appears in the second test. You should reset the ObjectFactory between tests following the Register Resolve Release pattern.
Another (preferable) option is to refactor your SUT to receive the IManager handler dependency explicitly via constructor. That would simplify both SUT and tests moving the ObjectFactory configuration to the Composition Root.

Moq out parameters

I'm fairly new to using Moq and Nunit for unit testing and I'm having issues with one scenario. What I want is for my mock to have an out parameters which my system under test will then use to decide what action to take.
My system under test is an MVC API controller and in particular I'm trying to test the POST method. I want to return an error message for the object when validation fails.
Here is the method code for the controller:
public IHttpActionResult Post(Candidate candidate)
{
try
{
if(candidate==null)
return BadRequest();
IEnumerable<string> errors;
_candidateManager.InsertCandidate(candidate, out errors);
if (errors!=null && errors.Any())
return BadRequest(CreateErrorMessage("Invalid candidate: ", errors));
return CreatedAtRoute("DefaultApi", new {id = candidate.CandidateId}, candidate);
}
catch (Exception)
{
return InternalServerError();
}
}
This is my Unit Test Code:
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.InsertCandidate(new Candidate(), out errors)).Callback(()=>GetErrors(errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(new Candidate());
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}
What I expect is that when _candidateManager.InsertCandidate() is run then the errors variable is populated. However what is happening is that when you step through the controller code errors is null after _candidateManager.InsertCandidate() method is run.
If anyone has any ideas what I'm doing wrong or if what I want to do is not possible using Moq then please let me know.
Thanks
What you want to do is possible. If you look at the Quickstart docs at https://github.com/Moq/moq4/wiki/Quickstart, there is a section where it shows how you do setups for methods using out params. I have made two corrections to your code and it works.
You have to use the same candidate instance for both the mock setup and when you exercise the sut. Otherwise, Moq thinks that the two objects are different and your test setup becomes useless.
You don't have to use Callback in order to set the errors returned by the mocked CandidateManager.
Below is your test method with my changes.
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
//instance to be used for both setup and test later
var candidate = new Candidate();
var mockManager = new Mock<ICandidateManager>();
//removed Callback
mockManager.Setup(x => x.InsertCandidate(candidate, out errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(candidate);
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}
You have to make sure that when you call your SUT that you use the same instance passed to the out argument otherwise the call will fail.
In your example, the method under test passes a null instance into the mocked method thus negating the setup of the test.
If however you are not able to supply the same instances for the out then it doesn't look like you will be able to get a mock to pass successfully. Take a look a the Quick Start for Moq to get an understanding of it capabilities.

Unittesting and mocking robotlegs service calls using Oil extension

I have an ExampleModel that calls to an ExampleService that retrieves data from our backend. I can't figure out how to write unit tests for my application; which is structured as shown below:
ExampleService
public function retrieveMyToDoList(parameters):Promise
{
var promise:Promise = performRequest({request: "call to backend", parameters: values, session_id: clientModel.sessionID});
promise.addResultProcessor(parseRetrieveToDoListResult);
return promise;
}
protected function parseRetrieveToDoListResult(data:Object, callback:Function):void
{
does some JSON parsing into an object
callback(null, object containing my retrieved data)
}
ExampleModel
public function getMyToDoList():Promise
{
var promise:Promise = exampleService.retrieveToDoList(parameters);
promise.addResultHandler(onGetToDoListResult);
promise.addErrorHandler(onGetToDoListError);
return promise;
}
private function onGetHeadrsByUserResult(promise:Promise):void
{
// where this event will be listened to by mediators etc
dispatchEvent(new ResponseEvent(GOOD_RESULT));
}
private function onGetHeadrsByUserError(promise:Promise):void
{
dispatchEvent(new ResponseEvent(BAD_RESULT));
}
I'm trying to use asmock to mock my Service so that I can test my Model and how it handles the various results in the resulting Object but how do I mock the callback? I saw examples where the return values were mocked but in my case I'm using the Promise and callback and I'm not too sure how to go ahead.
If someone could please advise.
Thanks!
You can let the mock service return a real promise and call the handleResult method of the promise directly.
FYI: it's not a good idea to have a direct dependency from the model to the service. You should let the service manipulate the model, or pass the results from the service to a command which will manipulate the model. Models should never depend on anything else than helper classes.

JMS Testing - JMSTemplate send not executed

I have code piece sending jms messages via Spring JMSTemplate. For testing the the method i use Mockito.
My code looks like following.... publishDialogueServiceMessage()->
brokerUrl = jmsQueueProperties.getProperty(MessageRouterConstants.JMS_QUEUE_URL);
LOG.info("The broker url is : {}", brokerUrl);
jmsTemplate.send(jmsQueueProperties.getProperty(MessageRouterConstants.QUEUE), new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
ObjectMessage obj = session.createObjectMessage(serviceResponse);
messageSent = true;
return obj;
}
});
In above code to i set boolean variable true, to check that if the message is sent
My Test looks following,
#Before
public void setUp() throws Exception {
connectionFactory = Mockito.spy(new ActiveMQConnectionFactory(
"vm://localhost?broker.persistent=false"));
conn = connectionFactory.createConnection();
conn.start();
}
#After
public void cleanUp() throws Exception{
conn.stop();
}
#Test
public void testPublishDialogueServiceMessage()
{
ServiceResponse response = Mockito.mock(
ServiceResponse.class, Mockito.withSettings()
.serializable());
JmsTemplate mockTemplate = Mockito.mock(JmsTemplate.class);
java.util.Properties p = Mockito.mock(java.util.Properties.class);
Mockito.when(p.getProperty(MessageRouterConstants.QUEUE))
.thenReturn("outbound.request.queue");
mockTemplate.setConnectionFactory(connectionFactory);
mockTemplate.setDeliveryPersistent(true);
mockTemplate.setSessionAcknowledgeMode(2);
mockTemplate.setSessionTransacted(true);
ReflectionTestUtils.setField(publisher, "jmsQueueProperties", p);
ReflectionTestUtils.setField(publisher, "jmsTemplate", mockTemplate);
// test
publisher.publishDialogueServiceMessage(response);
ArgumentCaptor<MessageCreator> msgCreator = ArgumentCaptor.forClass(MessageCreator.class);
Mockito.verify(p, Mockito.times(2))
.getProperty(Mockito.anyString());
Mockito.verify(mockTemplate, Mockito.times(1)).send(
Mockito.anyString(), Mockito.any(MessageCreator.class));
//MessageCreator msgCrt = Mockito.spy(msgCreator.getValue());
//Assert.notNull(msgCrt);
Assert.isTrue(publisher.isMessageSent());
}
In test i facing an interesting problem as publisher.isMessageSent() always returns me FALSE indicating that send message seems not executed(?). but Mockito.verify(mockTemplate, Mockito.times(1)).send(Mockito.anyString(), Mockito.any(MessageCreator.class)); goes fine.
I am wondering what is the cause that my messageSent variable not setting. Can anyone shed some light what I might be doing wrong.
Simple, you have a mock for the jmsTemplate (your mockTemplate). When a method is invoked on a mock it doesn't do anything other than record the call to the mock. So the mock doesn't know that it should attempt to invoke the msgCreator.
Looking at your test I see some obvious issues that suggest a lack of knowledge of Mockito. Why are you setting all of those fields on mockTemplate? It is a mock, it will not use those fields anyway. This also suggests that you don't need the code in your #Before and #After.
If you REALLY want your test to send a message via JMS (and thereby invoke the message createor) you should use a spy on JmsTemplate instead of a mock. However, I would highly discourage this as your test will be dependent on an external system and you would in effect be testing JsmTemplate. The fact that your mock gets invoked properly is sufficient. The only additional thing I think you need to do is to invoke the message creator being passed to the mock to verify that it creates the message correctly.