1

I am investigating how to test a project. Some information about the project:

  • Microservice architecture, with roughly 20 services. About 10 of them with a separate database.
  • We use ServiceFabric
  • There is a fair bit of service to service communication.
  • Each server is a separate repo. With no references to each other.
  • There is one common Nuget package, where some things like date to string. This repo does not have any references to any other repos

I would like to be able to write both unit and integration tests, but I am focusing on unit tests for now. The problem is better illustrated by an example.

Let say I want to test AggrigateAllOrdersForUsers in the OrderMs. The code looks like this:

List<AggrigatedOrderTDO> AggrigateAllOrdersForUsers (List<Guid> userIds)
{
var internalUserIds= userService.GetUsers(userIds); -> calls User ms via REST API
var orders = orderHistoryServer.GetOrdersForUsers(internalUserIds); -> calls OrderHistory ms via REST API

var orderTypes = constantsServer.GetOrderTypes(); -> calls Constants ms via REST API
var validTypes = GetValidOrderTypesForOrderAggrigation(orderTypes) -> internal function

return Aggrigate(internalUserIds, orders , validTypes); -> internal function
}

Orders in this case is a complicated object. It has reference to multiple other classes, where ids need to match. It is not trivial to create, and I would like to reuse the code for creating the fake data.

How would you go about mocking/stubbing/faking the external calls here? It is easy to create a fake for userService, but where do I put it in my source tree? Everything is separate. Do duplicate the fakes? Do I create a new userServiceFake repo? Do I go about it in a completly different way?

Kind regards

2 Answers 2

1

The way I've handled it in the past is to have each api in the chain have two client libraries.

One is the normal, 'connect to the api server' client lib. The second reads its responses from files rather than connecting to a server.

Both these libraries live in with the Solution which contains the api itself.

  • Integration tests, for that service, use the normal client. But I save the raw responses I get when I run the tests.

  • In my dependent services Unit tests, I reference the file based client nuget package, along with the Integration test generated sample responses.

If I have a chain of services all dependent on each other in this way I can follow the same pattern at each stage. In this way the file based client hides any sub dependencies.

Of course you could equally well implement an in memory mock client or something and publish that. The benefit of the file based approach is that

  • There is a lot of code reuse from the real client.
  • When the service changes you can quickly generate the new expected responses by running the integration tests
  • If you need a custom response in a particular unit test, you can edit the response file.
13
  • For this to work, Order needs a reference to User repo where User and UserFake is. Is this correct? In my case there is no such reference. There is no dependency between projects. Jan 21, 2019 at 15:03
  • in your example, orders are generated by orderHistoryServer which I presume is an api call to a microservice? in your unit tests you would inject orderHistoryServer_FileBasedClient
    – Ewan
    Jan 21, 2019 at 15:10
  • Yes. Does this mean that you run an instance of orderHistoryServer_FileBasedClient in your test enviroment? There is an actuall network request generated within the test? Jan 21, 2019 at 15:13
  • orderHistoryServer_FileBasedClient doesnt generate network requests. its just reads the expected response from a file.
    – Ewan
    Jan 21, 2019 at 15:14
  • userService.GetUsers is implemented in the Order server. It is a wrapper class which sends an async html requests to Order server. It knows where the user server is based on an enviroment variable. There is no common code between services Jan 21, 2019 at 15:21
0

This is a method that doesn't really do anything, besides calling other methods or functions. So really, you won't be testing any real behavior, since the actual work is done in all the other methods or functions. If their corresponding tests pass, then you know that this is most likely to work.

So, having said that, you can (and really, should) test the invocation of the methods here, to know that your overall process does work. You can assert the output of the method (given the input), but also (using your preferred unit testing library) make sure that all methods are called. Of course, the implementation of the other classes are to be mocked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.