Dev Consultant Julien Oudot demonstrates how to automate the testing of a data layer in ASP.NET Core WEB API with Cosmos DB.
The intent of this article is to describe how to automate the testing of a data layer in an ASP.NET Core WEB API relying on Cosmos DB.
Azure Cosmos DB is Microsoft's globally distributed, multi-model database. Sometimes referred to as a server-less database, the promise is the ability to be able to transparently and indefinitely scale your data with high throughput, low latency and high reliability.
To achieve this goal, an efficient design is needed along with a strong monitoring and testing strategy to give users the confidence that their systems perform as expected.
To efficiently test a web API controller relying on Cosmos DB, three approaches are usually observed:
- Mock classes responsible for the interactions with the Cosmos DB back-end. This option is efficient to unit test the controller logic but requires implementing mocks. Furthermore, it does not test the interactions with the back-end.
- Spin up a test container in the Azure Cloud (we will only consider collection in this article). With this option, users need to provision their Cosmos DB environment and collection on the flight in their Azure subscription. It is closer to reality, since users run their test cases against a full fledge version of Cosmos DB.
- Rely on the Cosmos DB emulator hosted in the Build agent. This approach avoids spinning up a true Cosmos DB collection and therefore, having to pay for its provisioning. Furthermore, it does not require much effort aside from setting up the emulator. This is on this strategy that the present article focuses on.
Lately, the Cosmos DB team published a new VSTS task that can automatically create an instance of the Cosmos DB emulator, hosted in a Windows Container and spun up on the Build machine. Then, users can parametrize their test projects to use this emulator instance instead of an actual Cosmos DB environment. This task can be included both in the Build or Release processes.
Setting up the code repository and Build process:
First, an ASP.NET Core Web API application hosted in VSTS is needed. If you want to reuse an existing application, you can clone the following repository and then push it to a VSTS repository as explained here: https://github.com/joudot/CosmosDB-EmulatorTesting-VSTS
Then, an existing VSTS environment is also required in order to push the code to the repository.
Finally, create a new Build process, click on Build – New, and then choose ASP.NET Core
It will come with the following steps to build, test and publish an ASP.NET Core application (the Restore step can be removed from the following list):
When we add the task to run the Cosmos DB emulator in VSTS, it will start the emulator in a Windows Container. That is the reason why we need to choose Hosted VS2017 which is a Windows agent.
Create the test project and configure it to point to the emulator URL
To be able to test this controller, we first need a test project. This Xunit project instantiates a Controller and run some tests.
It also contains a configuration file with the Cosmos DB configuration used to connect and run our test against the Cosmos DB data store.
Note that the authorization key is the default one used by the emulator. The endpoint URL will be overridden on the fly by the VSTS Build pipeline. For local testing, these fields could be overridden to point to a local emulator or to an existing Cosmos DB environment.
Create the emulator instance in the Build process
Now, we need to go back to the VSTS Build definition and add the new task: “Azure Cosmos DB Emulator”. Request the task and add it before the Test step.
This is a task that will spin up a container, hosting the emulator instance. In the next step, we will override the configuration, so that the test project connects to the Cosmos DB collection hosted in the emulator.
Update the configuration to connect to the Cosmos DB Emulator
When running the test cases from developer machines, the string CosmosEndpointUrl as well as the authorization key contained in the application settings file can be replaced by actual connection credentials. These credentials can be found in the Settings – Key section of the Cosmos DB environment in Azure. However, in the context of the Build process, we need to automatically override the endpoint URL value, since it will depend on the container id started by the VSTS task under the cover. This id will be different for each run.
Add a new PowerShell Task and put it between the Azure Cosmos DB Emulator and the Test steps. Select Inline as a type. Then, you need to replace the CosmosEndpointUrl string by the endpoint value that you can find in the output variable CosmosDbEmulator.Endpoint:
(Get-Content .\CosmosDbClient\CosmosDbTests\appsettings.json) -Replace 'CosmosEndpointUrl', '$(CosmosDbEmulator.Endpoint)' | Set-Content .\CosmosDbClient\CosmosDbTests\appsettings.json
Initialize the Cosmos DB Collection from the test project
Because the emulated Cosmos DB environment created by the task won’t contain any collection upfront, the collection initialization steps need to be implemented as part of the test project (or could also be isolated in an VSTS task using the Azure Command Line Interface). To do this only once for all tests, we can rely on the concept of fixture available in XUnit: https://github.com/joudot/CosmosDB-EmulatorTesting-VSTS/blob/master/CosmosDbClient/CosmosDbTests/CosmosDBFixture.cs
The method CreateReviewCollectionAndInitializeIfNotExistsAsync creates the collection and uploads the documents expected to be in the collection by the test scenarios. The advantage is that there is no need to think about cleaning up the data once the tests are executed because a brand-new collection will be created by the following Build iterations.
The last step is to go to the Publish step and uncheck Publish Web Projects. This will publish all projects contained in the code repository. By default, this step is going to look for a web project at the root of the repository which is not our case (the project is in a folder).
Now we can queue a new build and check that all steps are successfully executed.
It takes some time to pull the container image and start it. That is why, it might be more appropriate to use this testing strategy in the context of a Release Definition instead of doing this at Build time. Using private agents would mitigate this, since there would be some caching on the machine to ensure that we don’t always pull the entire image.
Overall, thanks to this VSTS task, we will be more confident that there is no regression affecting the interactions with the Cosmos DB data layer, since it will always be tested against the containerized version of the Cosmos DB emulator.
And the beauty of this does not require to create (and pay for) a new Cosmos DB collection every time we want to run the tests.