The course is part of this learning path
Test-Driven Development (TDD) is a discipline that helps to create error-free, quality code, and drives productivity by reducing and eliminating errors as well as providing many other benefits.
This entry-level training course is designed to bring you quickly up to speed with the basic features and processes involved in performing C# based test-driven development using xUnit. It covers the key principles and practices of test-driven development, which you can then utilize within your own software engineering projects.
In this course, you'll be guided through an end-to-end sample project in which we use test-driven development to build, test, and package a .NET Core 3.1 C# based library that will provide an API to convert Bitcoins into foreign currency.
If you have any feedback relating to this course, please contact us at firstname.lastname@example.org.
- Understand the fundamentals of TDD and xUnit
- Use TDD to build, test, and package a .NET Core 3.1 C# based library
- Perform TDD by performing Red-Green-Refactor iterations
- Learn how to use TDD to build, test, and package a .NET Core 3.1 C# based library
- Learn how to create unit tests using the xUnit testing framework
- Software developers interested in using TDD to build .NET Core applications using C#
To get the most out of this course, you should have a basic understanding of .NET Core and the C# programming language, as well as software development and the software development life cycle (SDLC).
Okay, welcome back. So in this demonstration, I'm going to introduce you to the concept of mocking. If you recall, in our previous demonstration, we updated our implementation to actually make live calls to the CoinDesk API to get real-time information.
Now, the problem with doing that is that it's introduced an external dependency, and that our unit tests are now failing because that particular payload that comes back is changing in real time. This particular demonstration is all about showing you how to mock out that particular dependency.
Let's begin. I'm going to jump back into the instructions first. So step four demonstrates and shows you how to use Moq, as a mocking library for our C# .NET Core unit tests. Now, to bring this in and to make this available into our unit tests, we need to add it to our test project. So I'll copy this instruction here. And this is the package, the mocking package, that we're going to introduce into our unit tests.
So back within the terminal, I'll shutdown the current dotnet watch command, and I'll now paste those commands. So this is downloading the mocking library, and it's going to make it available for us to then update our xUnit unit tests in which I will mock out the CoinDesk API calls.
Okay, so the download of Moq has now completed. Let's now update our unit tests. So first thing I'll do is I'll import Moq and also Moq.Protected. Because we're going to mock out our HTTP calls, I'll also bring in System.Net and System.Net.Http, and I'll also need to introduce System.Threading and System.Threading.Tasks. Save that.
Okay, the next thing I'll do is I'll do a quick dotnet build, just to make sure that everything compiles and that my test project has access to the mocking framework. Okay, so from here, I'm going to update our class by adding in the following constant. And this is going to contain a copy of a payload that comes back from the CoinDesk API.
So if we jump back into our browser, I'm basically taking a copy of this and storing it here within this particular private constant. And so when I run my unit tests and I've updated them with the CoinDesk API being mocked out, it will be this payload here that is constantly returned with these static values, thereby making our unit tests stable over the duration of their lifetime.
The next thing I'll do is I'll introduce another private field. It will be of type Converter Service, and we'll call it the mockConverter. I'll then add in a default constructor without parameters. And I'll set the mockConverter to be equal to, GetMockBitcoinConverterService. And then, we'll implement this. And this will be the main area where our mocking implementation takes place. So it will be a private method. It needs to return a Converter Service, taking no parameters.
So this is where the main implementation of our mocking takes place. And the background to the mocking, again, is that we want to simulate any external dependencies so that when our unit tests run, they are stable and they run within memory.
Now, rather than write this out by hand, I'm just simply going to paste in a code snippet that implements the mocking. So the background to this is that we're going to build a mock object that gets injected into any time an HTTP call is made to our API end point.
So a couple of things about this mock implementation. The main thing about it is the response. So what we're doing here is that we're setting the response to contain this payload. And then, that response is called upon any time the mock is called upon. I then establish a new HTTP client into which I pass in our mocking implementation. Therefore, anytime that client gets called, it's going to return that static response. And then, finally, I need to set up a new Converter Service whereby our HTTP client, which has our HTTP response mock data in it, is passed in. Finally, that converter is returned.
So when our unit tests start up, the default constructor gets called of which a mockConverter is created. And then, anytime our unit tests run, all we need to do is now call the mockConverter in place. So this is nice and easy, and we do it all for all unit tests. The final piece of the puzzle that we need to update are the unit test expectations.
So instead of these fictitious values, we need to place the real ones that we're using within our mock object. So if we look up USD in this here, we basically need to copy this value here. And then, update this. Remembering that this should be a double. So we do the same for Great British pounds. And then, finally, again for euro.
Okay, we'll save that and we'll do a dotnet test. So there's one more thing that we've forgot to do, and that is we need to update our actual implementation class, and we need to allow the constructor to take in an HTTP client to allow our mock to be injected. So I'll add an HttpClient. I'll then update the default constructor so that this client gets set a real HttpClient, but then also create another constructor that overrides the default one, which takes a HttpClient as an input. And this particular constructor allows us to inject our mock. Save that. And then, we need to update the GetExchangeRate method to not create a new HttpClient, but rather reuse the one that is created within the constructor. Okay, save that, rerun our dotnet test, and excellent.
We can see that those three unit tests have now passed. Six of them are still failing, but we know the reason why. We'll go back into our tests. And we basically need to update the in-line data attributes to use the real values. So let's update this now. So we know we need to use this value for the first one, this value for the first one of the Great British pounds, and this one for the first one of euros, and then the other ones are just that amount times two. So in this case, 22973.0682. This one is 17801.7386. And the final one is 19618.6556. So again, these values here are just two times this value, or this value is two times that value, et cetera.
Okay, we'll save that and rerun our tests. So you can see here that we're almost there, but one is returning an int and the other is returning a double. So the thing that we forgot is to update the signature on the unit tests method. So the convertedDollars should return a double instead of an int. And I'm pretty sure that when we rerun our tests now, everything will go green. Excellent.
So we have completed the last iteration of doing red, green, refactor, all our tests are now passing. And the great thing is our unit tests are using a mock, which mocks out the CoinDesk API, meaning our unit tests will be stable over their lifetime. Had we not, our unit tests would have been brittle, as the values coming back from the CoinDesk API are constantly changing in real time. And the other thing is that our unit tests are strictly unit tests, not integration tests, where we're making calls out to external components.
Okay, go ahead and close this demonstration, and I'll see you shortly in the next one.
Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, GCP, Azure), Security, Kubernetes, and Machine Learning.
Jeremy holds professional certifications for AWS, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).