Integration Testing .NET Core | How To Write Integration Tests C#? | ASP.NET Core REST API

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone welcome on the ifs and whites channel my name is philippe palux and if you don't know me i'm not the developer and architect i'm also co-founder of ifs and device company and not supportive application today we will talk about integration testing what's that when you should write integration tests and how to do this i will show you also why i think that this pyramid doesn't work for every solution and when you should write more integration tests than unit tests no matter what languages you use and what experience you have with writing tests i will show you a few tips that can simplify your life so don't be afraid that i based on.net core example because the idea is common for everyone so let's start it okay so let's analyze what we are going to do today i created the demo application for you you can find this application on our github so links below and this this is a simple api application where we uh where we handle http request then we send the command using rabbitmq then we handle that command we call external service in order to download some additional data and we save the results to the database and at the end of the operation we emit the event that operation has been completed so in my opinion this is a super typical use case today i will show you how to write integration tests which cover all possible connections but first let's talk a bit about tests probably most of you know what the test pyramid is the pyramid determines the ratio of the number of tests in the application it shows that we should have the most unit tests then integration tests and finally end-to-end tests it's hard to disagree with that but unfortunately this is not true in every case so let's consider when we should write unit tests i found one definition in the internet so let me read it a unit test is a way of testing a unit the smallest piece of code that can be logically isolated in a system a unit can be almost anything you want it to be a line of code a method or a class generally smaller is better smaller tests give you a more granular view of how your code is performing so let's simplify the definition unit tests work well when we have an input output scenario so usually it's called black box tests so we don't have in our logic and independences like database queue external apis etc such tests are perfect when we test algorithm or if our logic is completely isolated they are quick simple uh describe the solution really well and check that our algorithms are working correctly if you would like to see the perfect case for unit test definitely you need to check our github there's jaml project so you need to check because this is a perfect scenario for unit tests but what if our application doesn't contain algorithms and mostly is responsible for writing and reading from the database or queueing communications should we forcefully write unit tests the answer is no a well-tested solution doesn't rely on large number of tests but their quality incorrectly written unit tests really quickly begin to remind us of ourselves when we develop and maintain the solution which means that after any change in the code we need to rewrite dozens of unit tests in such cases it's much better to write integration tests there's one fundamental rule that we need to remember after code refactoring we cannot refactor the unit test the tests basically because how we can be sure that after our change we didn't break anything when we need to rewrite our tests also so we should only modify input and output parameters without reimplementation of body of that test i plan to record dedicated episodes about writing the unit test so let me know in the comment what do you think so let's back to integration tests so when we use integration testing we use a integration test when we want to test a given functionality with dependencies as a group usually the applications work with dependencies such as database queue external apis etc it's good to have a test that covers all connections to be sure that everything is working correctly together as a group if any connection is not working correctly our test should detect it automatically so here's the most significant advantage of writing integration tests because we don't use the mocks so we are 100 percent sure that our implementation is working correctly integration tests have a one more really important advantage because they allow us to test and debug our implementation really quickly in case of any back on production we can quickly create the data set up in our tests in the code and debug our solution based on real dependencies this approach simplifies the maintaining of the system and makes programmers life easier some of you may say that integration tests are long running and maybe it's better to choose the unit tests so answer is simple if you're logic based on the database and if your logic is strictly connected with database queues or whatever it's better to choose integration tests but if you are able to isolate your logic then you should use unit tests so you can use some concepts like onion architecture and ddd this is much better for unit tests because you are able to test your domain in in fully isolation from the dependencies the execution time of integration tests should be as short as possible long execution time can discourage programmers from writing the integration test do you like my films so don't forget to subscribe the channel and tell me in the comment what do you think it helps me create better content for you so thank you i often come across the statement that introducing integration testing is complicated and it can take weeks or months we need to configure build server set up all dependencies locally of course this is complicated task and probably it will take some time but at the beginning we can start with many assumptions of course containerization simplifies a lot because we can really quickly set up our dependencies but in case of older projects i recommend to start with many assumptions at the beginning we don't need to start all dependencies locally we can connect to shared database in the cloud or shared queue and limit the scope only on the logical level so we'll simplify everything and we'll save a lot of time integration tests are mainly supposed to help developers they are for them so it's much better to have something simple but useful than not having it at all okay so let's analyze what we need to do today like i mentioned i prepared the sample solution you can find the link to the github below and this is api application so in order to test this application we need to in within our test run the api this api call call this api so send the http request and then validate that our entity has been added to the database and that event at the end of the operation that even has been triggered so in order to do this we need to set up locally rabitomq and database so this is a postgres database i'm using uh docker to do this so on the github you will find the docker compose file additionally here we have an external api so in order to check that communication is working that our application is able to call this api we need to start own fake api and this api will be hosted in the process together with the tests and this api will simulate the real one so we'll check the real connection between our application and this api so this is everything from terry so let's analyze the code this is a api application dotnet core application i created the controller so we are going today create new expanse and um download expense from the database super simple functionality but in order to complicate a bit the use case i added the bass in the mid in the middle of of the process so uh when we handle create command using our api controller then we send a comma to our handler of course everything is hosted within one application and inside the consumer we call external service in order to get the exchange rate from uh polish slotted to euro and we store our entity to the database and then we publish the event so super simple implementation so let's analyze what we need to test here in order to check that our functionality is fully working i would like to check that i'm able to call this api so i need to host my api within the test then i need to check that our entity has been added to the database with proper exchange rate and at the end of the test i would like to check that event has been um triggered and basically that's all in order to check that our entity has been added to the database i'm going to use uh this method so i'm going to call api again and check that i'm able to download the entity because in test usually i try to simulate the real situation so i'm not going to call database directly inside my test i'm going to call api so let's go to the tests here we have a first first test i check if i can create the expense and this is exactly what i described first i would like to start my fake api service so i would like to simulate the real service but i would like to mock what the service returns then i call my service i create the expense then i'm waiting for the message in the queue so you can see that i'm waiting for the expense created event here and then i try to download from my api uh this expense and validate that data are correct there of course here we process this message asynchronously so i need to wait somehow for the execution we have a queue in the middle so we are not able to predict the timings so i will show you a few tips how to do this okay so let's start to to how i host our api so let's check the the real implementation this is how i start my api of course this this implementation is super simplified so i have a class responsible api host responsible for running my api so here i create the web host and i start that web host and basically that's all of course i inject like you can see configuration here all my settings are in app json app settings json file when i start the application i read from that file and i properly register my configuration to container i'm using auto fact auto here this is basically everything and what i want to do um in my tests i want to create one time setup um for my test so i would like to start my api also but only once her group of tests i don't want to start my api for every single test uh this is important here that my api is stateless so i don't store any state um within my api so i don't need to restart this api for every test so i created the test fixture file when i start everything and like you can see here we have a host fixture file this class is responsible for starting the api and what i'm doing here i create the host address i choose the host address for my my instance because it will be working on my machine then i load configuration and like you can see here i'm using different configuration file so this is this file so in order to distinguish somehow the configuration real configuration and test configuration i need to use different files and i need to inject different configurations so for my application this is transparent so instead of loading up settings json i'm loading up settings integration test json and i inject that configuration to my api host class in the next step i create database and i clean database if a database does not exist so this is also important and then i start my host so i start here my application my api and basically and basically that's that's all here then i need to configure the rabbitmq connection so what i'm doing here is i'm starting rabbitmq listener so this is super simple implementation and this implementation allows me to listen on chosen queue and here i configure listening queue so i'm able to handle this this event of course you you can take a look on our git cap and check the one step by step the implementation today uh we we don't have so many time to analyze every single line of code this is everything in the setup okay so let's analyze the the test implementation if i am here i'm sure that my api is working because uh my host fixture is responsible for the setup so when i know that api is working i need to start the exchange fake service so in order to do this i created the api mock implementation so this is again the api web host where i configure the endpoints based on input parameters so you you can say that i would like to start api with get for example method based on that url and for that url i would like to return some response every single time so look what i'm doing here i created the endpoint class and here i need to say okay i would like to return okay um http method get result here i pass the json or whatever you you need this result needs to be the same like in your real api implementation and then i need to pass the url so base for that url i will return what i configured here so i'm starting the api here also within the tests so like you can see and i am able to fake the real implementation but from the code perspective where i trigger the call this is completely transparent because look what i'm doing here i pass the url to my service i'm using the same path and i execute the action so when i run the integration test only this url will be different it will be my local host instead of a public api url so like you can see in configuration i pass different url down in normal app settings so for from the code perspective this is completely transparent and it allows me to uh test real implementation without any mocks in the code then when my fake api is is working and for choosing url i'm going to return the chosen value i call my api so you can see you can see i call my api v1 expenses create and i passed the comment here so this is this is a simple http call and then i get the id of my expense but in the meantime my implementation sends the message to the queue so somehow here i need to wait for for this execution like mentioned i'm not able to predict the number of uh milliseconds and minutes so i need to create some introduce some pulling mechanics you can pull database or you can wait for message in the queue or whatever you need in my case i uh i'm waiting for the message in the queue so i passed the message type here and i'm listening on the queue so like you can see i'm reading row message from the queue and then i try to execute my condition on that on that message to to check that this is exactly what i expect i need to pass checking interval in milliseconds so i'm checking every one second and here i have a retry account so how many retries i will i will check it 100 probably is too much it should be uh 10 or or less this is the only configuration you need to choose what you what you need after that timeout after that number of retries my implementation will throw exceptions so we will see that something went wrong so i'm waiting for for my message so i'm checking that id um here in the message is the same what i expect here and one when i will get the the message i'm able to call my api and download uh the expense from the api and validate that this is exactly what i expect based on also exchange rate mocked at the beginning of the test so like you can see this test is quite simple really good describes the behavior and really good test all possible scenarios all possible communication so i'm almost sure that everything is working in my implementation because i didn't inject any fake implementation i didn't catch implementation here in my opinion that's that's cool of course you can create more tests and for example validate your api responses here i'm checking that my api returns 400 for when expense does not exist so test is also super simple i start the api and then i call my endpoint to to get expense and i expect that i will get 400 for as a result of that operation so this is also great possibility to validate that your api is it's working properly like you expect especially if your api is used by another clients or customers i recommend to visit uh the github and check our code and step by step analyze the implementation it will describe you really well the concept and what i'm doing there so so that's all for today thank you and see you again bye
Info
Channel: ifs&whiles
Views: 1,395
Rating: undefined out of 5
Keywords: integration tests in ASP.NET CORE example, integration test .net, integration tests .net core, integration testing, integration testing C#, integration testing .net core, integration tests vs unit tests, integration testing example, integration tests example, integration tests C#, testing .net core, testing .net, testing C# code, testing C# web api, api testing C#, Asp.net core integration testing, .net core, asp.net core, rest api, C#, core, rest, asp.net, xunit, unit tests
Id: x2kfvBW58r4
Channel Id: undefined
Length: 21min 31sec (1291 seconds)
Published: Thu Jun 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.