Step by Step guide to refactoring, testing and mocking in Golang

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Music] so now I'm just going to clean up the workspace so we can actually work on the next thing which would be to test and then to mck as well I just need to close all of these open files and then we can actually proceed so of course um you might also have known but more or less what happens is that we didn't go it is a convention that if you have files like for example here I have API it makes sense that if I would want to test the component of um api. go file I would need to create a file next to this with the name API test. go so if we wanted to test our apis and would have this API test. go and then of course this can also have the same package but not more or less um test so that's um this is of course differentiated for from our main package and this is actually how we could have it for all our files so if we had like say within our config we had our helpers and maybe edmv here we can also create a new file and I can call this EMV test. go and again remember we can name it as helper tests like this and this is how we can individually test all our files and then of course test the functionalities within those files as a unit within our application but again also what I like to do would be if I have to test components of our app that of course spans across different units um which um can be like into integration test what I like to do more or less would be to create a folder and I call this Mock and then what I do is within mock I try to set up an environment that is very similar to what I have in here and what it means is that in here I'm going to have a file called app. EMV so it's going to have similar EMV files to the ones we have over here for example and then in here I can now create all the mocks that I want for example I could have one for DB and um this is where I would mock the DB um because again for this I may or may not want to depending on what I'm trying to test want to write into our actual database so there might be scenarios of course where it is really important to write to the actual database and maybe PGE it afterwards but just to be sure that that functionality is there um but for our own case I am going to mock the database and I think also this also quite also going to prove useful to you because I mean you can already write into a database for you that's not a problem and of course deleting an item from database should also not be a problem for you but marking might also may or may not be something new to you but that's totally fine so but anyways um I would like to create a file here and I can call this api. go now this is again going to be different from the one we have up here um because now I will like to test the overall API for all these different handlers um that we have here um so that's really the point of what I'm trying to do here and why do I have this in here inside handlers um API like this that doesn't make sense no that doesn't make sense I need to um one second I think I mistakenly moved that in there but I would like this to be out like this and this is how I want it to be so should be its own package API and then of course so this is how it should be so my bad I think I mistakenly dragged this into the wrong phot that's totally wrong this is how I would like the structure to be anyways so like I was saying I could go inside of our a mark and maybe the first thing I would like to do more or less would be to set up um the test and it's pretty straightforward I can maybe call create a struct here and I can call this um sorry API test and it's just going to be pretty straightforward it's just going to have a router and then this is going to be of type gen do engine like this and then I'm just going to have um a test um so this is going to be of type point out to testing T like this and of course this needs to go away so it should be like this and now what I would like to do more or less is to initialize my test so I can call this um API um test Handler like this so it's going to of course take a pointer to [Music] testing see and and we will not be doing any benchmarks we could I am not sure yet but that depends on how much time we have on our hands because I don't want this to be quite long so anyways I create my router and now I would really like to use the router that we created within this app I'm going to give it um um I'm going to give it version of V one or maybe I call it v z for for test and then my engine would of course be like this and now I can say new maybe API tests would now be of course um API test like this and now just need to pass router be router and of course T will be T like this and now what happens what I can now do more or less is to start writing the tests for um for this guy and why do I have this error so if I go to our main file I have yeah of course because of the move so this should be like this and now of course this should be like this what we can already do more or less would be to go to Main and of course I think this should be [Music] wrong should be like this um because now we moved things around so here I have um our main API and of course it's not returning anything actually and H what I can do yeah it doesn't really return anything so that's fine but um yeah I think that's totally fine um but I need to refactor this in such a way that it can return um something back to the user like more or less so what I have to do more or less would be to split this function into two but of course it becomes prevalent um as we move on that's this having out this as the definition of API actually is wrong because we have everything all bonded into this singular function and this would really prove difficult to maintain to taste and the rest because we have a lot of um in unrelated um functionalities or Bond into the same um function anyways sorry what I would like to do here would be to create an API I can just call it API like this um of course would be a struct and it's just going to have two things one would be an engine and of course this should be a router engine that we can get from go um from Gene and then the next would be of course a server and this will be of type HTTP server [Music] HTTP server like this and so this is um what we would like to have and beside this I would like a function that says um um maybe starts and and it's not going to return anything it's not going to return anything but it will be a receiver function um on our API struct and what it means now is I would like to create a couple more functions down here so I'm just going to duplicate this so I would like to have one that says init so this would initialize us server and then maybe I would have one that says um maybe with server because this will actually be um what would prepare the whole setup for us more or less and maybe I have another one that's would handle our middleware so I can say it's middleware like this and I think this is mostly what we would be needing so now I can I will really just go ahead and and split this into into couple of parts so maybe I start from here I would like to create a channel and this would of course be of type our API so this would help me to of course handle the initialization of our server and of course um when um when we more or less would want to terminate things this we can also of course use to um to of course listen to the um interaction from the user in the end um so but yeah at this point sorry what I would like to do here would be to set up our server and like you said it should be of type Comm on seriously HTTP server like this and then we can add some information here like address so address should be I'm going to use S print f for for Martin and then I'm just going to provide my string so it's still going to be like we have it the only difference now is I would like to specify a port and of course this should be at just 80 so that's really fine I think that's all we need for the address and yep I think that's looking okayish now of course I would like to set more information here sorry Handler would be a that's engine like this and read time out I'm probably just going to send this to set this to them seconds and we also need right time out and I'm also setting this to 10 seconds like this and now at this this point of course I need to return this of course to our channel so we we write into our Channel come on like this of course I need to write in a point from our app API and of course here need to pass this this way and then I would like to H I would like to maybe return API from here and then what it means is that we can get this of course again from the channel so we can use our iton keyword to read out of the channel like this and that's that's what we would have with our um with server um I think that's the point here is quite clear and the next function that we might have would be this um this um with mware so you should be able to take a version which of course is going to be of type string and because we have a bunch of middlewares we would like to return a slice of hand Handler fun or let this way and then we simply return return um and the funs like this and now we simply pass all this um base ones we had over here like this you can just copy this and put it in here and that's totally fine or you can also maybe move this to a new line for athetics for it to look nicer and everything but I think this is already cool and maybe I just collapse this so we can see all we are doing so with this function now we are trying to separate everything so with this function we able to Pro provide all our middle Wares with this of course we initialize the server more or less and now we we have two new functions left so we have the init and then we also have the um sorry and the start so would like to use start in the end to actually start the server um but what we can already do here um in the init would be actually to now move this over here so I'll just copy this and bringing this up here and maybe instead of defaults I'm just going to use new like this so I can also maybe even maybe what I can also do is what I can also do is to pass um this other default metal Wares um that we got from Gan like recovery and loger Logo like this and so that's at this point we simply just call this middleware and then we have everything we need so but anyways we have our so I will just put this function here a comment and I say add Med Wares and after this guy I would now need to populate our engine of course with what we just created and I think that's mostly what we would have and actually I can just go ahead and say something like um um maybe at this point yeah I can do some like a do use um yeah of course engine words one second yeah of course maybe before I assign this yes before I assign this I can say r. use and I can now say a do with with midle Wares like this and I need to um actually pass a version so what it means our unit should be able to take a version which should be of type string and then we simply pass this here and think that's mostly it but this version is not being used um okay so that means actually um we don't need this here for now so I can actually get rid of this here that's totally fine we would need this for the other middleware so that's totally fine and now we have set up our middleware we have initialize of course the engine and the next thing I would want to do now would be more or less to also create a function that's going to add our routes and I can call this again now you might start to get the picture of what we're trying to do but really just to we're trying to separate of course our the parts of our AP to separate functions anyways I can call this um add routes and maybe now it will make sense to actually provide the version and this should not return anything actually and I can just copy this guy put it in here and now we can of course simply do this this way a do engine like this a do engine a do engine like this and of course before we call this it means we should have already called this one um no we should have already called in it or sorry after this point because then we would be sure that if we get to this point to already have this populated so it means down here I can out routes and now I can say a do add rout and now I add my provide version like this and now after at this point when we've added the routes um we have this and maybe now of course we have our middle Wares and now I can actually call with ser because then this will also populate this guy so I can now say it with server like this and um I think that's totally fine and now but this function I would like to return to things more or less so it should be able to return the engine and an error of course and I'm just going to keep it simple so but it's going to return a pointer to engine and of course Arrow so what we call this in it we should be able to get these two things more or less and then um hopefully we have everything we need add Sero add routes with middleware more or less and I think that's totally fine and now we can actually now wrap this up so we can test and now for start we also really going to keep it pretty straightforward so it's is going to take a pointer to an engine like this and but the main thing is actually going to do for us was simply just to start this server and what we can of course do we can use um a function like this Co routine more or less to start our server and now we can simply do a server s server listing and serve and of course this returns an error so we have to handle it if error and this error is not equal to new I think it makes sense here to Simply Panic actually but I'll do it this way Panic F and now just listing and now provide actually the ports but I have to do it this way because I'm doing the string format modess [Music] and and now of course I have my error like this my piec is really really slow today as you can see down here it takes a while just to save a file which doesn't really make sense anyways I think um this is safe and do I need to add more information over here I think now we can actually do some maybe provide a way to listen to like uh inputs from the user maybe whatever but I think I'm I'm fine with this I'm going to leave it like this um I have a a video where I actually added a lot more to the API just to take care of all this other um all these other scenarios anyways but of course feel free to check it out and maybe what I can already do here is um to handle shutdowns so have it server and I handle shut down but of course for this I need a Contex so because if I over this you'll see it requires a context and then maybe what I can do is I get this from the contest um package as you can already see contest do um maybe with time out and this again takes um background content. background and I need to provide some time so I can make this two seconds so this would be um the time for my timeout but again if I over this I should get two things back the context and a cancel Funk so I'm going to name them like this contents Cano and then now here if I hover over this I need to pass a context and now I can pass this context but if I hover over it it returns an error so I need to handle this error so this if this error is not equal to new if this error is not equal to n again I'm going to do something quite similar to what I have here but the only thing might just be that the message would be different and I can say server shuts down something like this I don't know totally fine and maybe I put some trailing dots just to make it look cool and of course here I can already defer cancel that's totally fine and I think that's really all we maybe maybe sorry maybe down here I can now maybe print a message and say something like server is exiting something like this and I think that's totally fine if you want you can create another channel that you would use and listen to um inputs from the user like keyboard interrupt or wherever the case maybe but I think this is fine and now to bring this all together I would need to go into this in into this new method and now more or less redefine everything so I have API API would be of course API and I'm going to leave it as open like this and now of course say API do init what is going on what is going on come on I'm just going to commment S this out this is really insane so I going say API do init and when I call the init method I needs to pass a version so of course I get this from up here that's totally fine and now um my hover over this I get two things back so I get my engine and of course an error and you can already guess we have to hand handle every error so if this error is not equal to new I'm going to keep this one simple I'll just Panic that's fine for now and if everything is well we can now say API do engine would be R like this and we return API so what it means is that I have to return something from up here and new is to leine so but the cool thing is that we call init and init should be able H we see are not making use of our stats function and that's really not good so but we have to um make use of it actually so but what we can Now quickly do actually is if you notice I can now really get rid of this because um why did we have this again um open this session oh this was for for our when we had the session um it's fine I'm also actually going to throw it in here if that's fine cuz I don't want to lose this um that's fine I can put it in here and we can Rectify this much later that's totally fine and yeah that should be what what did we do with the DB uh we did Auto migrate and we can also do that again that's also fine and just to make sure this is in in in this is in it's compatible to what we had before so that you don't break things or you things don't break down when you check out this session just just making sure more or less um again you see this is really taking forever but it's can be super annoying so now I think we have everything so I can actually go ahead and wipe all of this and that's fine and now what we can do more or less is to go about to our main. go and now it's actually really going to be quite straightforward for us to move things around here so now what we can now do is when we call api. new we should get something back and that's our API and we can call it this way and now we can say a do start and now we pass the engine as you can see now this is much more modular and if we now can now go back and start writing our test and I can understand why I moved things around this way anyways I'm getting some warning from our mod file and I'm simply going to run a tid and that's really what we do now now we really jump into start writing our tests so I think right now we actually ready to start and um I think we have already wasted a lot of time so I'm just going to try as much as I can to make things as Snappy as possible here but still making sure I pass every possible information I would like to pass anyways I would like to create an API struct like we wanted to do before and it's going to have two attributes so one would be of course our um what did we call it initially was our router and this of course will be our engine router engine more or less and then we have test so this will again be our for testing like this and now what I can do is maybe I create a function down here and I can call this of course it's going to be a receiver function on API so API text like this and I can call this um test ping route um not it's not going to take anything anyways and or maybe I can call this test Miss so we test everything in in here all the routes under here we test all of them inside of this one function and the point is that in here now we can actually come over here and have our router as this and now we can now create our we can call this um API would now be API test like this and now we pass our router yes like this um nope I think this is already oh so this no do I want to call new here of course I can just call engine here that's also fine and now I can also call my T would be thisy so what it means now is to test my Miss Endo Miss Endo I simply call test Miss routes like this and now if I have more to test maybe I have um um test end point I would also add something similar I'm just going to copy this duplicate put it down here and then maybe all and then of course copy this put it in here um as as simple as that but of course to test the first one what would want to do more L was going to use we will use HTTP test HTTP test this one and I need to create a new recorder so this will be my new recorder and now I would like to of course Define the request I would want to make so this would be um making a HTTP Comm on HTTP new request and I need to provide of course um the method so it's going to be a get and it's going to be the URL is going to be think we had slash V1 slash now I think we yeah we had v0 up there but yeah we can also make it vely fine and then we go to miss and then we go to health like this and for the body here I can actually say HTTP that's no body if you don't want to do it this way you can simply provide meil but I like to do nobody think this is much more makes more a lot more sense so I get do things a request and an error so I can call this my request um I'm going to leave you to handle the error on this one and now I would like to of course make my request of course to my router so I'll say a router that's I now serve my HTTP and now I need to provide my response writer and of course the request like this and when the request has succeeded to test of course it's not just about making the request you will also need to verify the content of the request and the response being returned more or less so to do this I will use this uh asserts um um package like this do equal like this and let me see um what is the issue could not import of thought so let's do a go mode tid that's fine and we seem to have this but something is going crazy here okay so this seems to be cool one more time and I would like to of course verify a couple of things so if we looked at this Miss end points for this gets at least if we go to this Handler what does it do it's simply um one second no of course this was that what we had um I think this was really for our event handler was that what I really wanted to do anyways what I can do is to maybe come in here one second I maybe our API definition and down here um let's say in the init maybe at this point what I can do more or less would be to just to create some domain maybe I can clean this up cuz this is not supposed I'm just going to call this maybe event and I'm going to rename this to event event. go go and um I'm going to again call this at events rout and now I need to update some things maybe with middleware um no routes rather so add event routes and now I can now add my cuz I think I thought I had this Miss end points I'm actually going to rename this to events like this and now I can create a new folder and I can call this Miss and maybe in here I can add a one end point I call this health. go and actually what I'm going to do more or less would be to just get the base implementation from Gan so I'm going quickly because I don't want to waste time go Gan um so we go to Gan here and then we go to I's say get get Lear more documentation introduction quick start and yes we can actually just copy this and I'm just going to copy this parts and in here I can Define I can call this health Handler it takes again like before in context and what does it return something like this and I'm just going to remove this Parts because this is what I copied from gin website and what is the problem API of course event and now we can call this event event and of course we need to go into this event and change the package event and this one as well event and now we should be fine so this one should also be fine now exactly so we can now actually come in here and add a new file miss. go and I have to rename because I tapped that so fast that I messed it up um anyways I can copy this guy quickly find and come in here package API and I can now add my yes Miss routes and this would be Miss and I don't need this middle right here and this will just be at health and then we can now get this from Health Handler and then I can get rid of this of course we don't need to call the function so it should be fine like this and now if we everything should be fine yes so if we come back here we can now add no we go to our add yes add routes and we add Miss routes we have my engine and we have our version like this very straightforward and now if we come back in here um we have this at m/ health and now if we come back in here we can Now quickly of course make our request and now test what we get back from from this so what I can simply do is of course I need to pass um tests this way oh sorry wrong test and the next thing I would need to pass more or less would be what I expect so HTTP status okay I think this should be 200 and then the next 92 of course one to verify would be the code of the request so what we are doing more or less is to make start our server this way but then we make a request to this end point what which was what we defined over here and then we try to verify what we get back so we should if we look here again guys um this is supposed to return a 200 status code and I don't like to C on this like this I really don't like to status okay think okay should be 200 and so if I go back to our app should be yes okay and maybe what else I can test more or less would be the message we get here but anyways let's run the test first I can say go test raise and maybe I provide this this way and then fingers crossed hopefully if everything works fine then of course we we are on the right part so it seems our test passed if you look over here more or less you see um what actually has happened so um let's scroll up a little bit um so we had this in here on second um yes of course so we have our API which is done here of course we have a test file here about it's actually empty if you remember we had this in here um inside config helpers this guy but it's empty but even though it's prepended as test it still gets run but of course it's empty um but anyways there was the only one that was actually run and maybe what we can also verify um since we are doing this more less would be the response that we get back and to do that it's also pretty straightforward we do exactly what we have over here so we have assert um using equal of course if you look through there a lot of um functions that you could use and now I want to check the message that's returned and this message I want to get it from the response the body and I want to cut on this to a string so if we go back here this is what we should get message equal to pong and I will try to catch that here so it should be something like this and then here I need to the first thing would be message and out here would be of course P like this but again we need to escape this and now we can provide pong in here and where did the C come from so like this and now we can close this up and then run the test again maybe I set vet to off and then we run this one more time and now of course um this succeeds we don't get any error so what it like said initially now what we can see happen is we have the option to actually start our server and then make request to them so what it means is that you can make request to any of these end points try to simulate a failure you know so we can even do something like um um let's say um I can copy this for example put it in here and I can test maybe um I see something like test 404 routes and then here I can maybe provide something like nonexistent because we don't really have an endpoint like this and now we can actually test um not found for sorry I think it's status not found this one because this should be 404 and then we test the code and of course like I told you it's actually pretty straightforward all we need to do is say API um we say nonexistent end points and we say API test 404 route like this and then we can of course run our test one more time and we see this actually succeeds we've covered um how we can actually test our API again like I said um what we can do more or less is to go inside for each of these individual files create test files and test them all and um also I have a video already in the past where I try to explain how to do testing in case you not um familiar with this so you can always um come around and check this out if you're new to this I'm going to leave a link in the description as well so you can have this handy and but other than that the lesson I would really want to show you in this session more or less would be to show you how we can mock so in case for example we have this DB over here and we would like to mock how um the functionality what we can really do is because we working with SQL DB we can actually use xql mock from data dog and it's actually pretty um straightforward I can actually come in here and create a function and I can call this new database like this and um that's mostly what I would want to do and but what would happen the important thing is I would use SQL mock um from data do like this and I don't think I have this installed so need to of course tidy so I'm going to go mode tidy um to have this installed and of course this is quickly installed I can close this up so we have a lot more space to work with so we have this installed as you can see we get three things back we have SQL DB SQL Mock and of course an error and that's actually what I can have over here SQL DB Mock and errow that three things we get back and yeah we let's just handle this so if this error is not equal to nil and maybe we do a pH so log. Pho but I like to do a formatting more or less and I can simply add some some error here maybe I do something F like this as a SQL mock new and then here I would like to provide the actual message which I can do like this um but if there is no error I can actually down work with this as I did with my normal DB so what it means is that I can use my my SQL driver like this um my SQL the driver of course like this that new and what is this undefined my SQL of course I have this up here M now of course I need um this from gorm so I can get this from g. um slash driver and my SQL like this and hopefully I have this installed exactly and now I can now provide um the config so my S config like this and just like we've used the other one so con would be my SQL DB connection and then driver name I would just send this to my SQL like this and of course this should get me dictor so I can initialize this here dictor like this and now I can actually do maybe run some query maybe pain the database or something um but yeah maybe what I would like to do here is I open database con and because I would like to use G so g. open and of course I provide the D Lector and then I need to provide some more config like this and now just like we did before you can either provide more but I'm just going to leave this um empty for now um like this so this is fine and this would return a DB or error and I'm just going to copy this again bring this down here 200 this error and then now what I can do of course maybe would just be to go ahead and um return DB Mock and SQL DB like this and um of course we we are not returning anything from our function so I would like to return three things as you can already see um one of them would be of course the point to DP the next would be SQL Mark and the last one will be pointer to SQL DB like this and so that's actually one way we can use this and then with this more or less as you can see a poter to gum DB you can actually use it as we've been using it in our app um like here we are opening our connection we getting a DB here and then we use this and then what you can really just do is to go ahead um make insertions create tables and everything of course would be with this um would be with this um mock um because again like I said we would not want to write into our actual database that we have on the cloud on the aure side so that's why I'm keeping this of course within the mock um that we are doing right now so um you can of course test this individually whereby you insert into this database insert items and then query and then um try to ass what's like we've already done over here with this assert function to assert the response that you get back and everything and of course it should be pretty straightforward and yep if for some reason you are still having qu questions or problems with this of course feel free to um leave me a message and I can provide you further information um maybe I can show you quickly one way we can actually use this is maybe if we come over here um maybe I can just show an example in here let's say we have a function here and then we we let's say we are still doing all this under our API test and we would want to say let's say test um all end points or whatever I don't know or routes something like this I think we have one already yes of course so we can actually put it in here and so what we can really do more or less would be we can initialize our DB Mock and SQL DB this way and we can get this from our new database like this and then we have all of this three and now as you can of course see we have this instantiated already so what you could do for example would be maybe here already we can defer SQL go away SQ L DB not close we defer the closure of our connection and now what you can already do is mock use the mock this way that's expect begin and then after this we can we cannot do things like maybe create tables for example you can do expect a z like this let's say we would want to expect the creation of our user table I can do something like this create table and call it users and of course maybe I can do it this way to catch every possible thing so I don't need to specifically add every item that needs to be passed in here and then in the end of course I need to do Mark do expect commit of course um not close because of course when you've prepared your query and Med the query of course you you have to commit um this but what we can already do up here with our DB before we do this would be we do a DB dot use we use the migrator and then we maybe create a table and then to create a table we can actually pass models. user like this and so we try to create our table use this way using this um gum DB that we already have here wrapped over our SQL DB connection and then we try to use the migrator from come to create this table and then we try to assert if we actually made this query because again this in under the hood should make this query if everything is fine and then of course from here you can now go ahead and maybe call the endpoints like create user and then you Asser the query that was made and then maybe even make a query to the database try to F all users and see if actually something was written into dat and stuff like that there's a lot you can actually do with this um but I will let you try this out your yourselves and then of course give me feedback um I would like to know if it it actually worked out for you if you had problems whatever the case might be just please fre free to reach out to me and we can start a conversation around these topics anyways um it's already a long one but I'm glad I was able to show you most of what I wanted to show you today and we are actually more or less coming to the end of um this session we have like three more um or four more um sessions to go and then that will be all anyways guys that that will be all for this um tutorial of course again if you are new to our Channel don't forget to go ahead and subscribe and of course um ask me questions or leave comments whatever the case might be and until next time guys have a lovely evening and bye-bye [Music] oh
Info
Channel: Easy Dev For All
Views: 65
Rating: undefined out of 5
Keywords:
Id: 1o9HmaYjzh8
Channel Id: undefined
Length: 53min 9sec (3189 seconds)
Published: Mon Jun 24 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.