Unit testing camel routes containing serviceCall() - unit-testing

I have a camel route that reaches out to a URL and does some processing with the results:
from("direct:doURL")
.routeId("urlRouteId")
.to("http://host:port/path")
.process(e -> {
//Do Stuff
});
I can run unit test on that route without making an actual call to the URL by intercepting the call in my unit test like follows:
AdviceWith.adviceWith(context, "urlRouteId", a ->
a.interceptSendToEndpoint("http://host:port/path").skipSendToOriginalEndpoint().to("mock:test")
);
I’m updating that route to use .serviceCall() instead to look up the actual URL of the service, something like this:
from("direct:doService")
.routeId("servcieRouteId")
.serviceCall("myService/path")
.process(e -> {
//Do Stuff
});
This works great, but I don’t know how to do a similar unit test on this route. Is there some sort of LoadBalancerClient I can use for testing? What is the standard way to unit test routes with service calls? I've been googling around for awhile and haven't had much luck, any ideas?
Thanks

You can give serviceCall endpoint an id and then use weaveById.
from("direct:doURL")
.routeId("urlRouteId")
.serviceCall("foo").id("serviceCall")
.log(LoggingLevel.INFO, "body ${body}");
AdviceWith.adviceWith(context(), "urlRouteId", a -> {
a.weaveById("serviceCall")
.replace()
.setHeader(Exchange.HTTP_RESPONSE_CODE).constant(200)
.setBody().simple("Hello from service");
});
Alternatively if service is a bean in camel registry then you could probably override bindToRegistry method and use Mockito to create a mock service.
Also instead of using interceptSendToEndpoint it would likely be much simpler to just use weaveByToUri("http*").replace() instead.
Example generic method for replacing http-endpoints
private void replaceHttpEndpointWithSimpleResponse(String routeId, String simpleResponse) throws Exception {
AdviceWith.adviceWith(context(), routeId, a -> {
a.weaveByToUri("http*")
.replace()
.setHeader(Exchange.HTTP_RESPONSE_CODE).constant(200)
.setBody().simple(simpleResponse);
});
}

Related

How best to unit test a ServiceStack service that uses IServiceGateway to call other internal services

I've been following the guidelines here - https://docs.servicestack.net/testing
I'm trying to do unit testing rather than integration, just to cut down the level of mocking and other complexities.
Some of my services call some of my other services, via the recommended IServiceGateway API, e.g. Gateway.Send(MyRequest).
However when running tests i'm getting System.NotImplementedException: 'Unable to resolve service 'GetMyContentRequest''.
I've used container.RegisterAutoWired() which is the service that handles this request.
I'm not sure where to go next. I really don't want to have to start again setting up an integration test pattern.
You're likely going to continually run into issues if you try to execute Service Integrations as unit tests instead of Integration tests which would start in a verified valid state.
But for Gateway Requests, they're executed using an IServiceGateway which you can choose to override by implementing GetServiceGateway() in your custom AppHost with a custom implementation, or by registering an IServiceGatewayFactory or IServiceGateway in your IOC, here's the default implementation:
public virtual IServiceGateway GetServiceGateway(IRequest req)
{
if (req == null)
throw new ArgumentNullException(nameof(req));
var factory = Container.TryResolve<IServiceGatewayFactory>();
return factory != null ? factory.GetServiceGateway(req)
: Container.TryResolve<IServiceGateway>()
?? new InProcessServiceGateway(req);
}
Based on the discussion in the answer by #mythz, this is my solution:
Use case like OP: Test the "main" service, and mock the "sub service", and like OP I'd want to do that with Unit test (so BasicAppHost), because it's quicker, and I believe it is easier to mock services that way (side note: for AppHost based integration test, SS will scan assemblies for (real) Services, so how to mock? Unregister from "container" and replace w. mock?).
Anyway, for the unit test:
My "main" service is using another service, via the IServiceGateway (which is the officially recommended way):
public MainDtoResponse Get(MainDto request) {
// do some stuff
var subResponse = Gateway.Send(new SubDto { /* params */ });
// do some stuff with subResponse
}
In my test setup:
appHost = new BasicAppHost().Init();
var mockGateway = new Mock<IServiceGateway>(); // using Moq
mockGateway.Setup(x => x.Send<SubDtoResponse>(It.IsAny<SubDto>()))
.Returns(new SubDtoResponse { /* ... */ });
container.Register(mockGateway.Object);
So the IServiceGateway must be mocked, and then the Send method is the important one. What I was doing wrong was to mock the service, when I should have mocked the Gateway.
Then call the main service (under test) in the normal fashion for a Unit Test, like in the docs:
var s = appHost.Container.Resolve<MainService>(); // must be populated in DI manually earlier in code
s.Get(new MainDto { /* ... */ })
PS: The mockGateway.Setup can be used inside each test, not necessarily in the OneTimeSetUp.

Checking middleware is called from http call

How would one test that a piece of custom middleware is actually called from a standard HTTP event?
ie. The middleware is called from:
MyController.js
router.get('/some/endpoint', [myMiddleware()], (req, res, next) => {
// Code to do whatever here
});
The middleware itself can be defined as:
MyMiddleware.js
module.exports = () => {
// Middleware code in here
}
My quest is to check that the middleware is called once from my unit test, but I cannot find documentation around this.
MyTest.test.js
it('Should return whatever from GET call', () => {
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {res.body.should.deep.equal(bodyValue)});
// How would I place code in here to check that MyMiddleware is called?
// ie. sinon.assert.calledOnce(MyMiddleware)
});
I have thought about using Sinon's spy, but I can't think of how to hook into the middleware... My attempt was this:
const mwSpy = sinon.spy(require('path to middleware file'));
sinon.assert(calledOnce(mwSpy));
The usual way of going about this is splitting this into two tests, an integration test and a unit test.
Will the middleware I specified in the router.get call end up being called when someone hits this endpoint?
Does my middleware do the right thing?
The first part is basically testing that the Express API is doing what the documentation says. That's not what unit tests are for (this was tagged unit-testing), but since you are already using HTTP requests to test the endpoint, I guess that's not what you are after anyway: you are basically creating verification tests for your system.
You could still test the Express routing without HTTP, though, as I detail in the answer to this question, concerning how to test the router programmatically (faster tests, no http), but less just stick to what you have.
So the basic question is: "My quest is to check that the middleware is called once from my unit test". You don't seem to concern yourself with whether the middleware is doing the right thing or not, just that it's called, which calls for the question on whether we should test the middleware or the layer using the middleware.
In both cases, you need to find a way of injecting a test spy. Either you write a small utility method that will inject that spy: function setMiddleware(module){ middleware = module; } or you use some tooling like proxyquire. See this tutorial on Sinon's homepage for background.
I would just do this (in the test code):
it('Should return whatever from GET call', () => {
var middlewareFake = sinon.fake();
// I am assuming it's the actual app object you are referencing below in the request(app) line
var app = proxyquire('../app/index.js', { './my-middleware': middlewareFake });
//
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {
res.body.should.deep.equal(bodyValue)
expect(middlewareFake).was.called;
});
});

How to test routes in Laravel 5, or Trying to "MockStub" something, or I have no idea of TDD

I'm starting with TDD and Laravel. Specifically, I'm starting with routes. I defined some and I defined it badly, so excited as I was with the "new" concept of TDD I wanted to write some test for them.
The idea was to test the routes and only the routes, in isolation, as everything I've readed about TDD recomends. I know I can do a $this->call->('METHOD','something') and test response is OK or whatever, but I would like to know that the right method of the right controller is called.
So, I thought that I could mock the controller. This was my first attempt:
public function test_this_route_work_as_expected_mocking_the_controller()
{
//Create the mock
$drawController = \Mockery::mock('App\Http\Controllers\DrawController');
$drawController->shouldReceive('show')->once();
// Bind instance of my controller to the mock
App::instance('App\Http\Controllers\DrawController', $drawController);
$response = $this->call('GET','/draw/1');
// To see what fails. .env debugging is on
print($response);
}
The route is Route::resource('draw', 'DrawController');, I know it's ok. But method show is not called. In the response it can be seen: "Method Mockery_0_App_Http_Controllers_DrawController::getAfterFilters() does not exist on this mock object". So I tried to:
$drawController->getAfterFilters()->willReturn(array());
But I get:
BadMethodCallException: Method Mockery_0_App_Http_Controllers_DrawController::getAfterFilters() does not exist on this mock object
After some testing, I was able to arrive to this solution:
public function test_this_route_work_as_expected_mocking_the_controller_workaround()
{
//Create the mock
$drawController = \Mockery::mock('App\Http\Controllers\DrawController');
// These are the methods I would like to 'stub' in this mock
$drawController->shouldReceive('getAfterFilters')->atMost(1000)->andReturn(array());
$drawController->shouldReceive('getBeforeFilters')->atMost(1000)->andReturn(array());
$drawController->shouldReceive('getMiddleware')->atMost(1000)->andReturn(array());
// This is where the corresponding method is called. I can assume all is OK if we arrive here with
// the right method name:
// public function callAction($method, $parameters)
$drawController->shouldReceive('callAction')->once()->with('show',Mockery::any());
// Bind instance of my controller to the mock
App::instance('App\Http\Controllers\DrawController', $drawController);
//Act
$response = $this->call('GET','/draw/1');
}
But I would like to change the shouldReceives for willReturns: the atMost(1000) are hurting my eyes. So the questions I have are:
1) Is there a cleaner way to test ONLY the routes in Laravel 5? I mean, the ideal scenario will be one in which the controller doesn't exist but, if the route is ok, the test pases
2) Is it possible to "MockStub" the controllers? What's the better way to do it?
Thank you very much.
I've finally got it. You need a partial mock. It can be done as simple as this (the trick is including an "array" of methods to mock to Mockery::mock):
public function test_this_route_work_as_expected_mocking_partially_the_controller()
{
//Create the mock
$drawController = \Mockery::mock('App\Http\Controllers\DrawController[show]');
$drawController->shouldReceive('show')->once();
// Bind instance of my controller to the mock
App::instance('App\Http\Controllers\DrawController', $drawController);
//Act
$this->call('GET','/draw/1');
}
And, if you create a partial mock of all controllers in setup() method, all route tests can be grouped in a single (or a couple) of TestCases

How do I get a resource from an Angular.js module for a jasmine test

I have a module that contains resources for a project, and the code looks like this:
editor_services.js
var editorServices = angular.module('editorServices', ['ngResource']);
editorServices.factory('Project', ['$resource', '$http',function($resource, $http){
//...etc
now I would like to write tests for a controller that expects a project resource as an argument. How can I get an instance of the project resource that is created by this factory out of the editorServices variable?
Here is a working example how one would test Resources (or http) in angular
http://plnkr.co/edit/kK5fDFIVpyZTInH1c6Vh?p=preview
The basic setup is:
load angular-mocks.js in your test. This replaces the $httpBackend with mock version. See: http://docs.angularjs.org/api/ngMock.$httpBackend
In your test call $httpBackend.expect() to create expectation to be mocked out.
When you want to simulate server response call $httpBackend.flush()
There is a caveat that normal .toEqual() from jasmine dose not work with $resource so you have to create custom matcher like so:
beforeEach(function() {
this.addMatchers({
// we need to use toEqualData because the Resource hase extra properties
// which make simple .toEqual not work.
toEqualData: function(expect) {
return angular.equals(expect, this.actual);
}
});
});

MVCContrib Testing Route with Areas

I am using MVC 2 with Areas. To test routing, I am using MvcContrib.
This is the testing code:
[Test]
public void Home()
{
MvcApplication.RegisterRoutes(RouteTable.Routes);
"~/".ShouldMapTo<HomeController>(x => x.Login("Nps"));
}
I am not sure how to call routing definition that are stored in Areas.
Calling AreaRegistration.RegisterAllAreas() is not an option as it gives an exception.
Thanks
Revin
This is the way I do it which works for me
[Test]
public void VerifyRouteMapFor_Test_Area_TestController()
{
RouteTable.Routes.Clear();
var testAreaRegistration = new testAreaRegistration();
testAreaRegistration.RegisterArea(new AreaRegistrationContext(testAreaRegistration.AreaName, RouteTable.Routes));
"~/test/index".ShouldMapTo<testController>(x => x.Index());
}
Rather than calling RegisterAllAreas, you should call the AreaRegistration for that area you are testing. The RegisterAllAreas scans all the loaded assemblies and as a result does too much for a test. I would manually setup the test. If it still throughs and exception post it here or to the mvccontrib mailing list. I am sure that there are some cases where the TestHelper needs to be updated to support areas better. We have not added any specific area support to the test helpers yet.
For a unit test, perhaps it's best to just do the one area. But for an integration test, you'd want to test all the routes in the context, imo.