Cloud-First TDD with .net CORE — Part 2

Over in part one of this blog post we learned how to use the dotnet core toolchain and serverless to deploy asp .net core Lambdas to AWS. In this post we’re going to look at how to stitch these pieces together into a pipeline, and get this configured to run a bunch of tests on your API codebase before deploying it out.

Let’s start with a test

mkdir Tests
cd Tests
dotnet new classlib -f netcoreapp2.1
dotnet add package microsoft.aspnetcore.testhost -v 2.1.2
dotnet add package Microsoft.AspNetCore.App -v 2.1.2
dotnet add package nunit -v 3.10.1
dotnet add package NUnit3TestAdapter -v 3.10.0
dotnet add package Microsoft.NET.Test.Sdk -v 15.8.0
dotnet add reference ../App

This is going to create you a dotnet core class library and add to it all the dependencies you need to get going with aspnet integration testing, together with a project reference to our App. Let’s cook a failing test stew:

Hello Test!

In the above gist we’ve got a pretty basic test that’s going to leverage your existing Startup class and derive a TestServer from it. Using TestServer, you can host your Api “In Memory”, and make HTTP requests to it using the HttpClient newed up on line 10. If you run your tests now you should get a delicious failing test:

dotnet test

----- Test Execution Summary -----GivenHelloWorld.WhenIGetHelloMessage_ThenMessageIsRetrieved:Outcome: FailedError Message:System.Net.Http.HttpRequestException : Response status code does not indicate success: 404 (Not Found).Stack Trace:at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at System.Net.Http.HttpClient.GetStringAsyncCore(Task`1 getTask)
at GivenHelloWorld.WhenIGetHelloMessage_ThenMessageIsRetrieved() in c:\source\cloudfirst\Tests\GivenHelloWorld.cs:line 15

As we can see from our test output, the HTTP response returned from our call is a 404. Let’s go green, shall we?

All the above example does for us is to create an incredibly dumb ApiController with a single GET route that returns our string. If you throw this file into your Controllers folder, we should be able to get our test to pass:

----- Test Execution Summary -----GivenHelloWorld.WhenIGetHelloMessage_ThenMessageIsRetrieved:Outcome: Passed


Plumbing it all together

By now we’ve proved that we can test-drive out the functionality of our API, and that we have a route to physically deploy our thing. Our next step is to have our tests run after every check-in and, (in the event of them all passing successfully!), have a continuous integration pipeline deploy our code to a test environment. With that in mind let’s take the plunge into Gitlab Pipelines!

You’ll recall from Part One that we were configured 2 environment variables. These basically ensure that Gitlab is able to authenticate with AWS, and thus able to deploy our code out.

In order to let Gitlab know that you want to deploy your app out, you need to create a configuration YAML file similar to the serverless.yml we created in part one. Stick the following .gitlab-ci.yml at the root of your repo:

Let’s take a look at this in a little more detail

Lines 1–16 Define our build and test stage. If you’ve any familiarity with how Docker templates work, you might be able to spot some similarities. We’re leveraging the official microsoft dotnet sdk docker image here, and extending it by installing zip using apt-get. We’re going to be using this together with the dotnet lambda packageinstruction in order to compress our deployment artifact. We’re then going to copy this zip file to the root of our repo to make it accessible as an output from this stage into the next.

Lines 18–27 Define our deployment stage. As serverless is an NPM package, we’re going to go ahead and use the official node docker image (alpine distribution is super-lightweight, so this should speed up our deployment stage somewhat!) We then move the to the same folder as our serverless.yml, and then call serverless deploy as per part one of this tutorial.

Line 26 is quite interesting — Here we introduce a dependency on the first stage. This informs the order in which Gitlab is going to process the stages of your pipeline.

Running your pipeline

With any luck, you should now have:

  • A locally-tested API
  • A YAML-compliant .gitlab-ci.yml configuration file
  • A working Serverless template
  • Your AWS secret and private keys plumbed in correctly as gitlab environment variables (here, if you haven’t!)

Commit-and-push, eh?? Click through CI / CD -> Pipelines on the left-hand navigation menu for your Gitlab project, and you SHOULD have some happy little ticks like in the below screengrab.

CONGRATULATIONS! You just deployed an asp .net core lambda to aws using gitlab pipelines! 🙌

There’s obviously a ton of other features you could choose to look at from here. Things like Feature Flags to decouple launch from release, using environments to create different, segregated environments for QA. You can spend a while dipping your toes into the AWS service catalogue, or look at any number of the events that Serverless supports.

In a future blog post we’re going to be reviewing what we’ve built over this blog series, and extend it to create a pipeline for a statically-hosted website using Vue.js; But for now I really hope that you’ve found this instructive. All the code’s over → here ←.

Please, please shout if you’ve got any comments or queries on any of this stuff — it’s how I learn. 👋


-- dev, barbershopper, homebrewer and human (not necessarily in that order).

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store