How to write code that you can mock and test - Golang Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I welcome so in this lesson we are going to review how you can write go code that you can actually test and mock now we're going to use these repo you can find link on the description down below but what we are going to do here is basically create over here and your folder call just slash mean so we're going to work on this example over here so we have our main package and our main function so this will be the entry point of our application now let's suppose that we have in just to get started at this point usually when you start working with go you start by just throwing package functions everywhere so let's suppose that we are working with microservice in this case so so forth for you just for you guys still have an idea okay so what we are going to do here is just create common variable over here that we are going to call router and this one is going to be gained at the phone okay this is our router and over here router get slash pink and handler in this case is going to be pink and it is so we have our ping handler being a handler using jingling so in this case we are going to just return and string HTTP status okay and okay just like this so when we did here is just creating a new router creating a ping path over here and finally router that run and the 8080 port okay just as a simple microservice over here so as you can see here when we the application now we can curl absolutely something this a little bit localhost:8080 slash thing and there you go pong then actually here you can see we have a 200 ok and this is the response now this is what we usually start doing just creating package functions like this one and just throwing package functions everywhere now let's suppose that we refactor this a little bit like we did in our previous example so now we put controllers slash pinged controller let go and we put the handler the pink handler over here okay just by doing this so this is our ping gambler instead of putting all of these in our main file we just put this in our controller package over here and now the service is going to return services slash pink service and that's it so over here what we are going to do is create our handle ping and we just return the result being a straight so over here return pong and point being a constant like this ok and from our controller what we are going to say is services pink just like this and okay okay and from our main file we are going to define our handler as controllers not ping now if we execute this code you can see that the result is pretty much the same we can keep calling the same URL with a get request and that's going to work but now let's suppose that we want to change what this a service return okay so the first thing that we are going to do is create our controller test over here and by just finishing the file name in tests now this is a test case so in this case paint controller test so you have the file where you have all of your business logic and also your test file for the corresponding go file over here so as you can see I'm using the same name just to be sure and to be clear than in this file I'm testing the logic in this file ok now what we're going to do here is test the thing that we have over here so in order to do this test and the name of the function here test ping anteed being a pointer of testing dot T so as you know go has it's very own testing framework that we can use so this testing trackers is part of the standard library go so over here we have test ping and we can just run the test case and this is pretty much the same that going to the terminal go to test boot code controllers and as you can see here we have ping controller and ping controller test and over here we can say go test just over here and there you go the response that you're having here is pretty much the same that you have when running the test case over here so this is a test case for this function okay for the ping function so what we're going to do is create a context being Jean create text context like this as you can see you have the test context and we have our response writer okay so this is the response where we are going to put the response so over here we can put the response equals HTTP test you know recorder and pass the response over here so we're going to ignore the edge okay now since we're testing the ping controller like this we need to pass a context so that's exactly what we are going to pass over here we have a context and we trance this context to this variable now what we are going to check here is if response that code these things HTTP starts ok then T that fail response code should be 200 ok as you can see here when we're checking is after calling the pain function then the response code should be starts okay so as you can see here if we put or over here response code and we show this so you can take a look at this and after calling the function now we have the 208 100 now if we change this over here and we'll return a bad request you can see that when calling and testing this function now we have an error because before calling the function we have a 200 play here and after calling the pink function we have a 400 a bad request and the tests fail because we have this if over here now another thing that we want to test is the actual response body that we have so in this case we have response body that string and we can use this and do pretty much the same if response body string distinct pong in this case because that's what we are responding response body should say okay so as you can see here the only failing test case that we have is basically this one over here now let's go back to plot status okay and that's it okay we run the test case and the sequence e here everything works great so we can remove this now if we execute this test case with coverage you can see that we have 100% coverage in our controller over here okay as you can see here 100% statements this is pretty much the same but doing go test with the cover plug so these 100% of statements is pretty much the same that you are you're looking right here now if we go to the service and let me show you for a minute what we are doing here from our main function we define the router and then in the controller over here we are calling services handle ping and this is what we are returning so we are going to put here in just a message saying Ritalin doing some complex and after doing these complex things that we have over here will return the result so if we go to the controller and the test case and we execute this test case you can see that we have this output where we got the doing some complex things okay so we are actually calling the real method that we have in the handle pain now what if we are doing let's say 20 different things over here we're calling a database then we are retreating value from cache system that we have then we are using Kafka or RabbitMQ or any key messaging system that we have we are not doing a lot of really heavy processing over here and in the test case what we want to test is basically the controller okay over here we are writing a test case for this section over here for this single line of code but if the handle ping function does are really heavy processing and you have lots of recent logic in here then you need to mock this handle ping in order to test your controller ok like this now the problem here is what if we are returning in a string and an error okay and over here will return on a meal and we want to test this implementation so we have the response actually the result and your equals services handle thing and over here we are going to use if error distance new then we're going to return the error but stubs internal with near error otherwise the result with a 200 ok now the problem here is that we execute this test case you can see that we keep doing some complex things but if we execute this with coverage and how you can see that we don't have 100% statement but 75 okay because we are never testing this over here and we want to develop a test case that actually test this scenario over here so in order to do this we need to have complete and full control over what please handle pink function returns and the key thing here is that because go is a in a compiled language and it is well type and define language you cannot mock what this function actually does once this code is compiled okay so the first thing that we need to keep in mind when trying to develop code that you can actually test is that if you put you put a package function just like this you are not going to be able to mock this function okay because once this function is compiled you have no control over what this function actually does so the first thing that we need to do here is making sure that we always use interfaces where we want to mock later okay that's the first thing that we always need to have in mind now in here what we can do is actually creating a type that we are going to call pink service and this is going to be restricted okay just on like this top like this and instead of pulling this as a package function like this we are going to put this as a method to our pain service like this now from the controller as you can see here we have no way of accessing this have a pink because services and you have no public variables or functions in this package so what we are going to do is create our variable section where we are going to say that our pinging service equals a new instance of being service okay so from the controller you can say services that ping service that handle pain and if we execute the test case again you can see that everything keeps working because the only thing that we did is actually instead of having this as a package function we are defining this as a method to illustrate okay now the problem is that we want to change this behavior over here but we cannot do this because in here until now we have the same problem once this is compiled we have no way of changing what this handle pain actually does so in these cases you can see here we are never returning an error so if we have this code like this then you have no way in the controller to test this scenario that you have over here if you don't have any way of mocking what this handle ping actually returns now what we are going to do is create an interface in this case but we are going to call type ping service and we are going to call this interface as you can see here we have these two different types one being an interface and one being a struct and over here to the struct we are going to change this and say that this is the ping service implementation okay and this is the pin service now over here you will need to work on the implementation so the only thing that we did is create this thing service interface now an interface is basically a definition of behavior that you have that you need to implement in order for a given type to implement the center so in this case we are going to say that in order for any type to be considered a ping service it needs to implement a function called handle ping that takes no arguments and return and string on an error and as you can see here as soon as we define this you can see that this interface is implemented in our ping service implementation ok so this being service implementation struck that we have over here implement our ping service interface now as you can see this pink service is never used this interface is never used so the only thing that we are going to do here is just change this line over here what we are saying here is we have a new variable called ping service being of type ping service implementation and the value of this variable is going to be a new instance of this ping service implementation strategy ok so as you can see here the type can be omitted because go is inferring that the type based on the value that we have on the right over here so if we define this but instead of saying that the type of the service of this variable is ping service implementation now we can say that the type equals ping service ok so what we are saying here is create a new variable called ping service being of type ping service interface like this and the value of this variable is going to be a new instance of the ping service implementation now note that if I don't implement this interface then ACK I cannot use this stroke as being service because as you can see here the ping service implementation no does not implement the ping service because you need a handle ping that returns on a string and an error and you have a handle ping that returns just a restraint so you need to make sure that you implement a interface now the good thing about the interface is that basically every stroke that you have can implement this interface over here just by implementing this method now if we if we follow this and we go to this suit to the test case that we have now what we can do is create our type ping sir please mock like this and this is going to be an asteroid just like this now what we are going to do is implement our method in our mock so func mock being in service mode and we return and a string and an error and as you can see here as soon as we implement all of the methods defined by our interface now our pin service mock implements this interface and what we can do here is basically changing and saying that services the pain service equals a new instance of the interface mug like this ok so let's suppose that I want to keep returning this okay I want to have this test case running over it so I can return pond and new so when I run the test case you can see that now I don't have these doing some complex things and just returning mocking complex things okay so you can see that in here I'm mocking complex things and if I remove this from my test case you can see that over here I'm doing some complex things so what I'm basically doing here is creating a new variable implementing the interface and basically in my test case I'm replacing the original service that I have here by my own service because this pings RP smoke implements the same interface that we have defined over here okay so that's pretty much it so as you can see here in this case test ping and I'm testing no para okay but now let's see what's the case when I want to test ping with error so T being a point of testing that team and I want to do pretty much the same over here but in this case what I want to make sure is that a status equals status internal server error and the body equals ever okay now if I execute this test case you can see that I'm having this error because the response code should be a 500 and the the body shall say error but it is responding point with no error so the response code equals 200 so as you can see here if I execute this with coverage you can see that I'm testing this line over here thank God but I never testing this part over here and if the problem is that same as we did before this handle ping function while it is overriding what we have in our painter we still don't have any way of saying that this when this function is called it should return a different value okay we are still doing a mock but we don't have control over the mode so what we're going to do here is our handle being responder or function actually this is an attribute in our pin service mob and this will be a function that takes no arguments and returns a string and an error okay so over here instead of putting the hard code basically or the the logic that we want to implement what we're going to do is just mock that handle in function okay so we have a mock and what what we're saying is when you call the handle pink then return whatever this handle pink function is doing what we can do now is basically create our mock over here things have a smoke equals this handle thing over here and the service equals the mock okay like what we are doing here the paint sorry smoke equals actually our service service mark equals being service mock and services that think service equals our mock okay now in here what I'm going to test is the null error so sorry smoke that and I have this handle pain function equals a function that takes no arguments and return on a string and an arrow since in here I'm testing no error then return Punk and Neal okay as simple as that so let's take a look at this what we're doing is basically creating a mug defining what our function actually does and then saying that this mug in the function is equal to this definition in this way we can override what this function is doing when we call it okay now as you can see if we do pretty much the same on the or the other case like this now what we want to test here is testing with error so in this case as you can see from the controller if the error this things new they get the error that we get when calling this pink service kind of thing then return and status internal server error and the error string so in our test case we are going to say error calling actually executing pain and since this is what we are expecting in our test case that's exactly what we are going to put over here so in this case we are going to return an empty string and over here errors that new and this is the error now take a look at what we did here we have a controller like this and we have two possible scenarios after calling the handle pain we have an error and we process the error or we don't have an error and we use the result so we need to make sure that when writing test cases for this function over here we have complete control over what this handle pin function returns now in this way we are testing the first case so we create our mock being an instance of our stroke that we have defined over here then we define what this function actually does and what we expect to happen when we call the handle pin function this function is executed in the handling method that we have over here now as you can see here if we execute this with coverage now we are testing this case okay as you can see here we call the handle pin error distinct angle and we have status internal server error and the error actually if you want to have this display you can see result and you can see the earth that we are actually getting from the service so as you can see here you have no result and error executing thing and this error executing pain is what we defined in the mock that this function will return when it is cold okay you know executing thing actually we change this and you put some random stranger you have seen that this error failed because response you'll say error and we are getting error executing ping third in else instead of error executing thing okay because this means that we have over here this thing and the message that we have over here okay so as you can see here it is really really easy for us actually if I execute all of these good coverage you can see down in the controller that now you have 100% coverage like this because you have to find your service in a way that you can actually mock and take a look at this okay it is really easy and the only change that we made is basically this this is what usually people do when they start working with go okay in the best case they use a method over construct but usually you see things like this okay just Bacchus function everywhere and the problem is if you develop in this way then you are going to have a really hard time trying to mock this code and then you are going to start saying go it is really hard to test you cannot develop test and go if you want to make sure that the quality of your test of your code is actually and complaining with the requirements that you have you shouldn't be using go it is really hard to test and go and all of this is because you're using the language in the wrong way now take a look at the changes that we made over here first of all we implement the functions not as a sparkers functions but as methods over and Stroke okay like this and finally we define an interface like this that defines every method that we need to implement in a stroke to be in a type in a type basically to be considered tapping service and then from the mocks you just define a new stroke and you implement that interface and in every test case that you have the only thing that we need to do is basically write your own interface specify the behavior that you expect when calling these functions and that's pretty much it so as you can see here it is really really easy for you to have complete control over what your code actually returns so this is what you are going to have on the wrapper so you can find this repo on github I'm going to have the link on the description down down below so I really hope that you have enjoyed the video if you have any questions or any comments please use the questions or this section I'm always responding the new comments that I have and of course if you have any other topic that you'd like to have it covered hey let me know write a message over there and I'll be more than happy to help you with that okay if you like the content please subscribe to the channel I'm going to make sure that I publish new videos every week and of course I'm always listening to my community so if you have any questions please let me know so I can help you okay I really hope that you have enjoyed the video and see you next time
Info
Channel: Software Engineering
Views: 6,766
Rating: undefined out of 5
Keywords: golang, how, test, mock, unit, example, tutorial, testing
Id: b13zwMPLklE
Channel Id: undefined
Length: 33min 29sec (2009 seconds)
Published: Tue Mar 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.