Golang / Go Crash Course 04 | Unit testing our code by Mocking with Testify

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's going on guys? Today we are going to unit test our application by mocking some components in order to isolate the modules that we want to test and I created a Golang cheat sheet that you can download from pragmaticreviews.com ( it's free :-) ) there is a link in the description below on a previous video we refactored our API to implement a Clean Architecture approach so we created all these interfaces we created an HTTP router interface a post controller interface a post service interface and a post repository interface we created a router interface so our application is independent of the HTTP framework we created two implementations for the router interface one using MUX and other using CHI but we can also create our own implementation of that router interface this HTTP router delegates the requests to the controller layer and the controller collaborates with the service layer and the service layer is the place where the business logic is implemented the service layer interacts with the repository this repository could be a Firestore (from Firebase...) database MongoDB, MySQL Dynamodb, Postgres or any other database this repository could be also a mock repository and that's what we are going to use to isolate our post service in order to test it and we are going to use testify to create the mock repository that is going to be an implementation of the post repository interface remember to share like and subscribe to the channel and let's get started.... Ok this is the application that we've been working on the previous video and here is the service that we created basically we implemented this post service interface with these three methods Validate, Create and FindAll so we are going to create tests for all these three implementations so here we have the Validate implementation here is the Create implementation and here is the FindAll implementation we are going to start our test by doing a test for this method and then we're going to move on these two for the first one we are not going to use any mocking but for this we are going to mock the repo and here we are using the Save function from the repo and here we are using the FindAll function from the repo so in these two cases we're going to need to mock the repository and in this case we don't need to mock anything ok let's create a new file this is going to be the unit test file and the naming convention here is "_test.go" so in this case is going to be post-service_test.go ....and we are going to use the same package that's going to be service and here we are going to start adding our tests the first test is going to be for the validate method actually this method here and we have two cases here when we receive an empty post or nil as the post argument and when we receive an empty title like this so we are going to create two test cases one for each scenario so the first one is going to be TestValidateEmptyPost ...and here we need to import the testing library from golang like this import "testing" like that... and here we need to pass a reference to that library so this is going to be t and the reference *testing.T like that so first we need an instance of the service I'm going to crerate a variable here test.. service and I'm going to assign it to the "constructor" function that is NewPostService() and I'm not going to pass any repository remember that let's go to that function and remember that we need to pass the repository here .... I'm going to pass nil because we are not using it within this method okay and now I'm going to call the validate method this validate method let's go back again this validate method is going to return an error in case the post is empty or nil or if the title is empty so testService.Validate(nil) and we're going to pass nil so in this case we are passing an empty post or a nil post and here what I need to do is I need to add the assertions so I'm going to use the assert package from the testify library so the testify library will allow us to use not only the mock package to create mocks but also the assert package to create assertions on the data so I'm going to install it this is go get and the location is.... github.com/stretcher/testify ...okay now that we have that library installed I'm going to import it first I'm going to import the assert package and I'm going to use it here assert.... and within this package I get a bunch of methods to make assertions I get Equal, EqualError, EqualValues, Exactly, Fail, False, FileExists... a lot a lot of methods I'm going to use notNil()... and I need to pass two arguments the test library and I'm going to pass the error because in this case I know that or I expect to receive this error and now I'm going to add another assertion on this message so it's gonna be assert.Equal(..) and I need to pass the testing library and the error...I need to use the error function here this returns the message the error message and then the message that I expect I'm going to save this and I'm going to run it and the test is passing okay... if I let's say that I change this message and If I run this again this should fail yes it fails because this is what is expected and this is what I am...No (sorry)... this is actually like this first the expected value and then the actual value like that I'm going to run this again and yes it fails because the expected value is this one the one that we entered here and this is the actual value that is the result of this of the evaluation of this function here so if I change this and I run this again now the test passes... okay and now I'm going to create another test that is going to validate the title so I'm going to use an empty title in my post and this should throw an error actually this error... this message the post title is empty so let's do that okay and here I'm going to create a post let's go back to the entity... and let's see the struct... so the struct has three attributes an identifier a title and a text so I'm going to create that this entity.Post... and here I need to pass the identifier it's going to be 1, the title that I'm going to keep it empty and the text is gonna be "B" let's say... okay now I'm going to create a service I'm going to copy that and I'm going to call the validate function again like this but in this case I'm going to pass the post like that now I'm going to create two assertions like these two.... and again I'm going to expect that the error is not nil and in this case I'm going to change the expected value of the error message because it's different this case is so let's go back to the service in this case is "The post title is empty" I'm going to grab this and I'm going to change the expected value by that error message okay I'm going to run this test and I get an error why?... yeah because this is a reference to the post yes now I'm going to run this again and the test passes okay and now let's change the expected value and let's run this again and yes we get an error because we are expecting a message that is different from the one that we receive that is "The post title is empty" I'm going to change it again I'm going to run it and this is going to pass okay great okay now we are going to create our mock repository so we need to import the mock package from testify like this and first we need to create a structure and this structure this mock repository structure is going to implement the post repository interface actually this one so first let's create the struct type MockRepository struct and here I'm going to use a structure provided by the Testify framework that is this one mock.Mock and here I need to implement these two functions so I'm going to grab this I'm going to copy this here and now I'm going to implement these functions so this is going to be mock MockRepository ....and this needs to be a reference and I'm going to capitalize the first letter here like that okay and the same for the FindAll() function..I'm going to copy this here okay and here what we need to do is actually stub these functions returning the arguments that we receive and how can we do that?... so the arguments we can access those arguments by using mock.Called() and this is going to return the arguments and now what we need to do is we need to return from this function the arguments that we receive so in this case the first argument is going to be the post so basically we can access the first argument like this args.Get(0) so that's the first argument and we are going to return that result and we need to make a type assertion here that is going to be .(*entity.Post)... like that and the second argument that we need to return is gonna be an error we return the post and we need to return the error that in this case is gonna be args.Error(1)...let's save this and that's pretty much all we need and the FindAll function is going to be the same we need to receive the arguments and return those arguments so I'm going to copy this and the only thing that changes is this we don't return a reference to a post but we return a post array let's start by testing the FindAll function actually this is the one that we are going to test so we are going to call this method and we're going to create an expectation on the mock repository to call this function so let's do that this is gonna be func TestFindAll and here you need to pass the testing library and first we are going to create a variable that is going to be a mock repository... and here we are going to create a new reference to that repository it's going to be new and it's going to be MockRepository like that now we are going to setup the expectation..... and we're gonna say that we expect that our mockRepo when the FindAll() method is called what I mean is here on the repo we have the FindAll() interface so when this method is called that is implemented here by this mock we expect to return an array so this is going to return an array of posts and nil as the error and here we need to pass one element so I'm going to create a post to add it to this array I'm going to grab this one and I'm going to assign a title let's say "A" and I'm going to pass it here post when the FindAll() method is invoked on this mock repo is going to return an array including this post element here and it's going to also return nil as the error okay now let's create a service it's gonna be testService := NewPostService(mockRepo) and here we are going to pass the mock repository as a parameter and now we are going to call the FindAll function from this service and here we expect a result and we're going to ignore the error and here I'm going to create some assertions the first one is going to be an assertion on the expectation that we created here so this is gonna be mockRepo.assertExpectations(t) ....and we need to pass the reference to the test library so this is a mock assertion so this is behavioral and then we are going to create a data assertion that's gonna be assert.Equal and here we will pass the test we are going to pass the result that in this case is an array so we are going to get the first element of this array and we are going to use the title but first we need to pass the expected value that is going to be "A" in this case and the same for the rest of the attributes right we expect "B" as the text and we expect 1 for the identifier I forgot what the comma here here and here let's save this okay I'm going to create a variable here it's going to be var identifier int64 = 1 this is gonna be identifier and here I'm going to add the same in the assertion identifier like that and let's run this test...I need to remove this actually and let's run this test and now the test passes great okay so the assertion is passing so when I call this FindAll function of the test service is calling the mock repository and once we get the result that is actually an array including this post we are doing the assertion on each of these attributes we expect identifier to be 1 the title to be "A" and the text be "B" let's debug this test so we can see the details on how it runs okay now we are on the first line this is the reference to the testing library that we receive here and this is the mock repo so let's move on to the next line here I assign the identifier here is the value and here I create the post variable and here I can see the values that I assigned to that structure and now we're gonna see that we are going to assign this final expectation so I'm going to move on to the next line and here here is our expectation on the method "FindAll" I'm going to move this so we can see this better... here is the method here's the argument yes we are calling the FindAll method with no arguments that's why this nil value here and the return arguments are going to be the first one is going to be the post title actually an array with one element and the other one is going to be nil that it's going to be the empty error or the nil error... let's move on and now we should have only one element that is actually this post with these attributes okay let's move on and now we have 1 as the identifier and here we have 1 in the first position of this array we have this post and the identifier for that element is 1 and the title as "A" and the text as "B" so let's to move on and that's pretty much .....I'm going to forward this and the test is going to pass again okay I'm going to close this let's go back let's remove this breakpoint and now I'm going to create a new test for the create method if we go to the service we need to create a test for this function okay let's go back and I'm going to call it func TestCreate(...) and here as usual we need to pass the testing argument like this we are going to use the mock repository we are going to use a post because we need to pass the post to that repo and here we setup the expectations on the mock repo ... and here we need to pass the name of the function that is "Save" ... if we go to the repository interface we're gonna see that this is the function that we need mock the "Save" function that is actually this one here this is implementation in our mock repository let's go back when the "save" function of the mock repository is invoked this is going to return the post actually a reference to the post and nil as error like that and now we need to create our test service again that is the service that we are testing and we need to pass the mock repo again and I forgot to add the identifier here ok now we're good I need to call the create function of the service we need to use the service ...... and we are going to pass the post we pass a reference to post actually and here if we go to the definition of the interface this is going to return a post and an error so we need to handle both results result and we are going to ignore the error we're not going to use it and here we need to add the assertions so the assertion for the mock repository is going to be mockRepo.assertExpectations(t) and we have to pass the test and then we are going to do some data assertion...assert.Equal(...) and we're going to pass the test...and we are going to pass the expected value in this case we can test the identifier for example we expect this value from result.ID and we can do the same for the rest of the attributes so we expect "A" as the title and we expect "B" as the text of this post okay and we can also add another assertion let's assign a variable here and we can make an assertion on that variable let's say that assert.Nil(...) and we pass the error because we expect this error to be nil yeah I forgot to add the test.... like that okay let's run this one okay here I forgot that the identifier is a random identifier that is generated here on the service if we go to the service if we go to the save actually to the create method this is going to call this random function so we can do two things we can mock that library that's one option and we set the value that we expect when we call this method that's one option and the other option is changing the assertion I'm going to do that I'm going to change the assertion and I'm going to expect a not nil value.... and I'm going to expect that the identifier that is generated by the rand library is not nil so I'm going to run this again I need to remove this actually I need to only pass the title and the text here and I'm going to run this and now the test should pass great yes now we know that the identifier is generated and that's pretty much all I have for today, Thank you for watching, and I see you guys in the next video, take care, bye!
Info
Channel: Pragmatic Reviews
Views: 19,357
Rating: undefined out of 5
Keywords: golang unit testing mocking testify, golang crash course, golang tutorial, go tutorial, go testing mocking testify tutorial, learn golang, golang restful api, golang unit testing, golang unit test mock, golang testify mock, golang testify assert
Id: uB_45bSIyik
Channel Id: undefined
Length: 31min 40sec (1900 seconds)
Published: Thu Jan 16 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.