Check out a free preview of the full Building APIs with C# and ASP.NET Core course
The "Unit Testing the API" Lesson is part of the full, Building APIs with C# and ASP.NET Core course featured in this preview video. Here's what you'd learn in this lesson:
Spencer demonstrates how to unit test the APIs. A separate project is created for the tests, and the employee API project is added as a dependency to the testing project. Four tests are written to test the endpoints. The tests can be run from the terminal with `dotnet test`.
Transcript from the "Unit Testing the API" Lesson
[00:00:00]
>> Spencer Schneidenbach: Let's get into what I consider to be a very exciting topic. And I know that a lot of developers don't get excited about tests, but I feel like they're really important. So you notice that already we did a little bit of refactoring as we went. The first bit of refactoring that we did was, okay, let's group all these APIs together, put them under route groups.
[00:00:17]
So we demonstrated route groups and then I tested them ,right, I fired up the application and I started hitting the postman and I started hitting the endpoints to make sure that they still worked. But that feedback loop kinda sucks, especially when you get into API projects where they get dozens and dozens and sometimes hundreds of APIs.
[00:00:35]
So I think tests are the best tool that we have in our tool belt, to kind of mitigate that right? To provide a really tight feedback loop and also protect our code bases from bad refactors. So we're gonna talk about tests. The first thing we gotta do is have a test project.
[00:00:52]
So all tests have what's called a test project. So we're gonna go here and we're going to go to Solution Explorer, we're gonna right click and we're gonna hit, here, right here. We're gonna hit plus, and you're gonna see that we can create a new .NET project. We can also right click and see new project either is works.
[00:01:15]
So I'm gonna say that. So this is our solution file. This is our SLN file. This one is our project file, we're gonna go to our solution. We're gonna scroll all the way down to X unit test project. So we're going to use X unit for this. There is an MS test, there is an end unit, but even Microsoft mostly uses X unit, so it's the one that I recommend.
[00:01:35]
But to be honest with you, if you're writing tests, that's really all I care about. So we're gonna call it TheEmployeeAPI.Tests, hit, enter, put in the default directory. That's just fine, and then create the project. Yep, go, let's do it. You can also choose to do this through the command line interface, but I'm unapologetically a UI guys.
[00:01:56]
I'm going to do it through the UI and the first thing that you're going to see is that we have a file that looks like this. It's a unit test, and it's a very, very, very tiny blueprint for a unit test for this project. Let's break down what we're seeing here.
[00:02:12]
We see a pretty boring class with a pretty boring method, but it has this attribute at the top of it. It's Fact, and we're basically establishing this is ground truth. And this is the thing that X unit will use to say, okay, this is a test. This is basically our way of denoting this as a test.
[00:02:31]
We go to test here and we do, let's do open our terminal with Ctrl+Shift+tilde, make sure we're in the right directory. I'm going to call .net build
>> Spencer Schneidenbach: And you should see the tests come up here in this little beaker icon. You'll see that once we built, it found our test and we can run it even inside of the UI.
[00:02:56]
>> Spencer Schneidenbach: I also do .net test a lot. You'll see that me do that several times and you can see that our test passed. Well, it's 'cause it's not really doing anything. It's not throwing an exception. It's not saying, hey, this test failed, which is what we expect, which is good.
[00:03:10]
Next thing we need to do is add a reference to our project, our main API from this test project. So, we're gonna go to add project, we're gonna right click on the project file for the test project. We're gonna click Add Project Reference and it's gonna say, what do you wanna add a reference to?
[00:03:27]
And we're gonna say the employee API. And now that's gonna give us access to all the goodies here so that we can actually write tests for this API. Next thing we're going to do is we're gonna install a NuGet package to this test project. And this is going to be probably my favorite testing NuGet package for ASP.NET Core.
[00:03:46]
There are essentially two major types of tests, unit tests, which can test individual behaviors, and then integration tests, which do their best in a perfect world to execute tests for as real as possible throughout an entire system. This testing package is really cool because it's gonna allow us to hit our APIs.
[00:04:07]
It's gonna allow us to test our APIs exactly as our user is gonna use them. So this actually, under the hood will spin up a separate web server that the tests will execute against, which is really cool, and it happens very fast. So, we're gonna go here, we're gonna say, we're gonna open up our command thing here, and we're gonna say a little bracket to give it a little command.
[00:04:33]
I'd hit command+P for that, by the way. And we're gonna say focus on new get view cause I recently used it. And I'm gonna go ahead and paste the name of this package in here and you're gonna see that it's the first thing that comes up right here at the top, which is what we'd hoped for, and then we're gonna go ahead and install that to our test project.
[00:04:50]
Boom,
>> Spencer Schneidenbach: It's great, perfect.
>> Spencer Schneidenbach: So now, we have almost all the pieces that we need to run this application. But we do need to make a slight change to our employee API project because we're using top level statements and we don't have a defined program file. We could, in fact, just go ahead and convert to a program main style program, which we can do here.
[00:05:18]
I don't do that for this course but if you're using top level statements, you just have to put in a program declaration here at the bottom. So we're just gonna go ahead and put a public partial class program. And that's gonna mark our program class as public. So that way it can be accessed do the test project.
[00:05:42]
Now we can add our test class. I'm just going to copy paste this in. Boom, go to our test. Boom, take out this whole class definition. I'm gonna see some squiggly lines. It's because we need to import some things. I don't do anything that I don't. I just let the tooling do the work for me.
[00:06:00]
So you can click on that and hit this little hourglass or the little light bulb. And then you could say, using Microsoft ASP, NET Core testing, pretty easy so far. So a couple of things about this. I class fixture is basically an instruction to X unit to say, hey, I need you to create a copy of this.
[00:06:19]
And then I need you to if it's disposable, which in this case, web application factory of program is then destroy it at the end. So it's just basically a way for us to tell X unit, hey, you need to construct this thing. And then web application factory itself is the testing abstraction provided by Microsoft's Testing framework to basically say, hey, I wanna be able to run and start a copy of this program.
[00:06:47]
So it's very useful. So let's write our first test. So we're gonna call it a fact. We're gonna say public async, because, as we covered in the previous course, we make HTTP requests with a HTTP client, and we do it in an asynchronous way. So we're going to say public async task, get all employees, returns, okay, result.
[00:07:11]
Do that. And then we're going to create a copy of our client. So we're going to save our client. I'll be more explicit just for illustrative purposes. So HTTP client. Client =-factory, and you'll see that we have a bunch of stuff on this factory. We have a reference to the server, which we won't have to use too often.
[00:07:33]
We have a reference to these services so we can actually get services out of this thing. We can access our dependency injection pipeline here. And we have this method here create client which is exactly what we want. And you can see that it is indeed an instance of HTTP client.
[00:07:48]
So now we have something that can interact with the server right in the exact same way that our clients for this published API would access what same way we access it through postman. So we're going to say var response =await client.GetAsync. We need to provide it a route.
[00:08:10]
So we'll say slash employees and then we want to say response dot ensure success status code. This will basically throw an exception if the status code is anything outside of a 200 to 2 99, you could also use some of the of the assert class that's provided by X unit Assert.Fail, for instance or Assert.True, you can say response.is success status code.
[00:08:38]
Because that's a, it will tell you if it's a 200 status code or not. Either is fine. I try not to use ensure success status code in other code outside of tests, simply because I like to handle my HTTP client responses explicitly. Now we're gonna actually run our test.
[00:08:56]
So I'm gonna go Control+Shift+tilde, and I'm gonna say.net test.
>> Spencer Schneidenbach: And you're gonna see that it actually did pass the test, which is exactly what we wanted to see. And just to show you, this is what it's executing. We can actually go to our test thing here and we can actually debug our tests, which is occasionally important when you wanna see exactly the behavior that's going on, and you can see that it executed it it's a re it executed against a real endpoint, and the status code was indeed 200 which is what we want.
[00:09:30]
It's perfect. It's exactly what we wanted. So that's great. That's our first test. So now what do we get from that? Well, we get a way to test this thing in a repeatable, really fast way, without having to go and do it ourselves every single time. Gives us some protection as we're refactoring.
[00:09:48]
We gonna do a lot of refactoring. All right, so we've written a test for our first endpoint, which is really exciting. So now, let's scroll down and let's get the fact classes for the rest of our endpoints are the fact methods, I should say, go to our test class and paste them in, and then we'll talk about them.
[00:10:09]
First things first, you're going to see some squiggly lines. The import statements are right here. We just need a couple of additional things, mainly ones around using methods that are of available on HTTP client, the read from JSON method, actually. So let's talk about this. So first things first, I think we overrode our other test.
[00:10:33]
Let's put that back. So let's take a look at the other behaviors that we're actually testing here. So you can see that we are Testing our Get Async, and we should expect a success status code, because in our API definition here, we've started our list of employees with an ID of one.
[00:10:52]
So because we have set it up that way, we set our part as they say, the system under test and the system under under test comes with two ID employees with that ID by default, you can see that we indeed get a success status code back. Here's a slightly different one for our post request.
[00:11:11]
So I'm going to put this on a next line so that it can kind of all be together, and you're gonna see that our client Is created here, but now we're doing a post request and we're actually saying to HTP client, post this as JSON, this is actually an extension method to say take this body of this thing that I've put, convert it to JSON for me, serialize it to JSON for me, and then send it out to the post endpoint.
[00:11:38]
And you can see that success status code back, and then down here, we have our CreateEmployee, but we're just passing it in a blank object. And we don't want, as we mentioned, as we demonstrated earlier, if we don't have a first and last name, it's gonna complain at us.
[00:11:51]
So we do expect that the bad request status code will be sent back. And you can see, we do that with Assert.Equal to say, we expect the status code on this response to be bad request. And you can see, if I build my solution, actually, I'm just gonna call .nettest.
[00:12:14]
>> Spencer Schneidenbach: You're gonna see that all four of our tests pass, because that's the behavior.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops