BrandGhost

Testing How ASP.NET Core Registered Routes Are Configured [xUnit]

In this video, we'll look at how we can write functional tests in xUnit to assert different characteristics about the routes that we have registered in our ASP.NET Core application! For more videos on programming with detailed examples, check this out: https://www.youtube.com/playlist?list=PLzATctVhnsghN6XlmOvRzwh4JSpkRkj2T Check out more Dev Leader content (including full in-depth articles with source code examples) here: https://linktr.ee/devleader Social Media: YouTube: https://youtube.com...
View Transcript
in today's code example I want to walk us through how I was writing integration tests to assert the different apis that I have in my asp.net core application and how I went about querying for all of the registered routes and then how I wrote tests to actually go with cert that things like rate limiting and authentication were all set up as I would expect so let's jump over to visual studio and I'll explain more in detail in my previous asp.net core videos I actually used a sample application but in today's video I'm actually going to use a real application that I've been building and I figured this would be a really good example to walk through because it's actually a real thing that I'm trying to do for my own API so I'm going to gloss over some of the details that we'll see on the screen here just because they might not be totally relevant but in case you're interested in mimicking some similar behavior I'm going to point them out and then that way you can go look into these more in detail or ask questions about them in the comments and I'm happy to answer more for my tests I actually love using an X unit I know there's a million different flavors of testing Frameworks but for me X units always check the boxes so I do have a couple of traits which are just really these different attributes we can assign for X unit tests I just have them for my own integration suite and honestly at this point because I've switched over to Azure devops I don't really need these but this was for a different platform where I had less luxuries to work with in my particular case because my application needs things like different databases set up and I want to make sure that I'm not accidentally tampering with production databases and being able to write to those I actually have these different test fixtures set up and because I'm actually querying my API in a lot of my tests I have this other class fixture as well which just gives me a web client to go interface with it so these are all very specific to my needs and then if we go down a little bit further you'll see some of the setup that I use in my integration test and again I'll just quickly gloss over it but we we can see the fixtures being passed in here so these just need to correspond with these eye class fixtures up top and that's an X unit thing and then what I do is I use this Builder pattern where I can actually have this nice syntax where I can say that I want to use a local server port and then I can go actually configure my application to use my MySQL container and a mongodb container so behind the scenes I actually have Docker images through test containers spun up to go make this work and that works really well for all of my integration tests and then I do some authentication setup and then this other part down here is just so that I can wire in some of my app startup stuff to actually query for my routes and in particular this is actually the one of the most important parts out of all of this setup code that is going to be relevant for the rest of this video but I wanted to touch on all these other pieces just so you can understand what's going on and if you think it's interesting then you can ask about it so why is this part really useful for us well let's jump down to this class which I have down here and we can see that I have this ensure rate limiting and this is really the test Suite that I'm trying to set up here and I have this interface called I web app Post Run work and in my particular case this ends up getting called once I start my asp.net core application and because I'm running that asynchronously in this other method that I have I go run any registered I web app Post Run work interfaces that have been registered through Auto fact and once the app is started up I run all of these that are registered and then I await on the task which is the app being started and the reason that I do this is because when we want to go check for the endpoints which I have in a previous video I demonstrated that the method I'm using which I will scroll down to right here actually requires that the application is started if we try to run this particular code any point before the application has started I don't get back any endpoints and what I have observed is that because I'm using minimal apis in asp.net core a lot of the other methods that I saw online that seem to be all of the stack Overflow answers suggested things that just don't work with my minimal API registration so being a little bit frustrated this is the solution I came up with and just to kind of circle back all of this ends up getting registered through Auto fact and when my application starts up it goes through the list of all of these that have been registered this iweb app Post Run work and in this example I only have this single one and what it's going to do is when it calls this do work async it will go ask for all the endpoints and this is where we actually have the body of our test now when I say that you might have caught that if I scroll back up we're inside of a private class here and if you're familiar with X unit you might be saying well Nick that doesn't make any sense at all how could you possibly have an X unit test inside of a private sealed class here because you need to label all of your X unit tests with either a fact or a theory attribute and you're right you do but if I scroll up a little bit further we can see that I actually do have this fact and I'm using a bit of a hack for how my application is set up but if I go call this particular line of code the instantiation of getting an API client for me actually enforces that I have a web application started up in my case instead of trying to expose the web app and doing some funky stuff I actually have all this other integration test stuff set up so that when I want an API client that talks to the web app I actually have to start it and because this was already written the really cool part is if I just ask for this API client it will literally go start the server to go get me this API client and just by starting the server if you think back to what I said a few moments ago that means that this has to go get run so yes this is a little bit of a hack in your case if you were able to get the web app you might be able to say VAR app equals and then I don't know how you would go get it if you're using Auto fact you might have a lifetime scope you could do something like lifetime scope or a lifetime resolve and you go get your your web application I don't know what the class actually is but you might have something like this right and then after you do app.start I guess it's I think it's called run and this actually blocks so you can't use this you would have to use this type of syntax here you say run async and then we would get the routes here and then you would await on the task here or you could do something like uh app dot I think it's stop is the syntax as well so um anyway the point is that in my particular case this is a bit of a shortcut and because you don't have the same code base as me in the same setup if you want to accomplish a similar thing you might want some syntax like this again I don't know if you're using Auto fact or whatever else you want but you need to get an instance of your application set up and then I'm just gonna to demonstrate my point you would have something like this here and then write your test thank you autocomplete for that so yeah it would look something like this okay so you likely want some type of setup like this but that's not what I have and I'm just gonna now that I've made that point to actually go through and show you what my um body of the test is actually asserting in this particular case so if we scroll down when we get these endpoints for my particular application I only want to be writing some tests that operate on my post and get methods I do have I don't have anything like delete or put or any other HTTP Methods at all so I'm not worried about those for now but I do actually have some signal R stuff registered and that ends up showing up on some routes and right now I don't want to assert that stuff so I actually will go skip over anything that is signalr we can see that down here I have this check for is not sync signalr but what I am interested in is actually checking for rate limiting attributes so when I've been writing my routes I actually want to ensure that everything that I have registered actually has one of these rate limiting attributes on them the reason that this is really important for me is because if I want to be able to expose my API for public consumption and let's say that I want people to pay for it I want to ensure that everything is properly rate limited now the other challenge that I have is because I work a lot with plugins in my particular case I have a lot of vertical slices for my web app and all of them are organized into plugins and each plugin is able to register different routes so because it's kind of distributed I want it to have a single spot so this is actually an integration test that will run on my main app not per plug-in and it will go create the whole app with all of the plugins loaded that I would have in production and then it will go ask for all of the routes and then what we're trying to do is checking to see if we've properly configured each route with rate limiting so I just have a check here that says if we have basically more than one rate limiting attribute which is not going to make any sense it will compile I think it actually runs but I don't know what the expected behavior is so I just enforce that we can't have more than one and then if it's not signal R and we don't have any rate limiting attributes I also want to fail and make sure that I can basically stop myself from accidentally committing any routes that don't have rate limiting this test is actually pretty simple when we look at what's actually happening under the hood but the complex part was really just that I needed a way to be able to query for the endpoints and because of how all of my application is set up if I scroll back up a little bit more I have some of that buried in here but like you saw earlier in the video I demonstrated how you could go new up a new instance of your web app and then essentially go run all of this stuff right inside the body of your test so hopefully you found that useful I found that I've been writing more styles of tests like this recently to basically go check over all of the different routes that I have on my application so this example I demonstrated the test that I'm using to go look at rate limiting I do plan to have the same thing for authentication although I do have a couple of routes that are not required to have auth on them so I need to find a way to go balance that properly and then the next thing that I'm doing is actually trying to make sure that I have documentation properly set up and I'm going to make a follow-up video on that which I will end up linking to this one and what we're doing in that code is actually going to look at the Swagger documentation that's output and linking it in a similar way to the test we just looked at and ensuring that we have the right XML Define in our output so thanks so much for watching hope you enjoyed and we'll see you next time

Frequently Asked Questions

What testing framework do you prefer for integration tests in ASP.NET Core?

I love using xUnit for my integration tests because it checks all the boxes for me. There are many testing frameworks out there, but xUnit has been my go-to choice.

How do you ensure that your tests do not affect the production database?

I set up different test fixtures to ensure that my tests interact with separate databases. This way, I can run my tests without the risk of tampering with production data.

What is the purpose of the rate limiting attributes in your API tests?

The rate limiting attributes are crucial for me because I want to ensure that my API is properly configured for public consumption. This helps prevent abuse and allows me to manage how users interact with my API.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload