Nick Chapsas
Posted on September 17, 2018
Introduction
Don't we all love well tested code? I know I do.
It's just an awesome feeling knowing that if I change something over here, the thing over there didn't break.
However, when your code depends on a third party library, then you have to hope that the code in that library is easy to work with and easy to test.
If this isn't the first blog of mine that you read then you probably know that I'm working on an open-source ORM for CosmosDB called Cosmonaut. When I'm using a third party library and its code happens to be opensource then the first thing I do is to go to wherever it's code is hosted and check if there are tests covering it. Nobody wants to use untested code, so I knew when I started that I should cover as many scenarios and cases as possible.
Unit testing
The CosmosDB SDK is not as friendly as it could be when it comes to unit testing (and the CosmosDB SDK team knows it, so it will get better).
Normally, the way you would unit test such code would be to mock the calls that would go over the wire and just return the dataset that you want this scenario to return.
However here is a list of things that make it really hard for you to test your code.
Problem 1
The ResourceResponse
class has the constructor that contains the DocumentServiceResponse
parameter marked as internal
. This is bad because even though you can create a ResourceReponse
object from your DTO class you cannot set things like RUs consumed, response code and pretty much anything else because they are all coming from the ResourceResponseBase
which also has the DocumentServiceResponse
marked as internal.
Solution
To solve this problem you have to somehow set the response headers for the ResourceResponse
. As you might have guessed already, then only way you can do that is via reflection. Here is an extension method that can convert your generic type object to a ResourceResponse
and allows you to set the responseHeaders
.
This extension method is also part of Cosmonaut
Problem 2
The SDK is using IQueryable in order to build a fluent LINQ query and use it to query your CosmosDB. This LINQ query will be then converted into SQL via an internal LINQ2CosmosDBSQL provider that the SDK comes with. The problem with that is that if you want to mock your IDocumentClient
's CreateDocumentQuery
to return a specific data set based on a LINQ expression then you're in for a ride.
Solution
Lets take a look at the code.
As you can see, it needs quite the setup. That's because the IQueryable
needs to be setup from the ground up or else it won't be properly translated.
Problem 3
It's basically the same as Problem 2 but for SQL, which makes it slightly easier because there is no IQueryable to setup.
Solution
Almost everything in the code from "Problem 2" is the same. The only differences are between the lines 24-47. You need to replace those with the code below.
These 3 are the most common unit testing problems you will face with the CosmosDB SDK. Thankfully the other operations are easier to test, especially if you make use of the ToResourceResponse
extension method.
UPDATE: As I was writing this Microsoft released the 2.0.0 version of the CosmosDB SDK packages and in them they changed the constructors of come classes.
If you are using a post 2.0.0 version of the SDK, here are the extension methods you need.
Integration testing
Unit testing is awesome, but you also want to test your system against a real database. These tests should have a wider scope than the unit-sized context of your unit tests. I won't go in depth on this one because everyone's idea of integration testing seems to be different but what you need to remember is that we are trying to test again the real CosmosDB database.
As you probably know, CosmosDB comes with it's own local emulator. That's great because we can use it to run our integration/system tests against. What's also great is that we can use the same emulator as part of our CI pipeline.
Automating unit and integration testing
I personally use AppVeyor because it is incredibly easy to setup and get started, it supports loads of stuff such as Azure services and Nuget building out of the box and it is also free for open source projects.
AppVeyor is as simple to setup as linking your Github account and selecting the repository you want it to run against. Once you set that up it will trigger a build every time you commit code. There are tons of stuff to configure if you want something more specific but in this blog I will just explain how you can setup the CosmosDB emulator in AppVeyor.
AppVeyor will look for an appveyor.yml
file in your repository. If it's not there it will use the settings that can be found in the "Configuration" section. Our appveyor.yml
will be pretty simple. We just want to dotnet build
and dotnet test
our code. Before that however we need to install the CosmosDB Emulator on the AppVeyor VM. In order to do that we will need the following powershell script:
Now the CosmosDB Emulator is installed and you can enjoy testing against it using the default CosmosDB Emulator url https://localhost:8081
and the well known key C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
.
Here is the final appveyor.yml
Once you set that up, a build will be kicked off every time you commit code in the repository and both unit and integration tests will be run against the local CosmosDB emulator instance.
Happy testing!
Posted on September 17, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.