Getting Started With Unit Testing | XCTest | Swift

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's going on everybody it's your boy kilo logo and today we're gonna be going over a unit testing now I'm gonna actually dive into how to use a more realistic approach on getting started so if you haven't started writing unit tests then I'm gonna essentially show you how to get started and this isn't gonna be like I'm gonna show you how to increment a number in a test which is totally unemployed hopefully my example will be a little bit more real-world so that you can actually start adding tests into your project starting today all right so the very first thing that we're gonna do is we're gonna take a look at this project right so I just want to break it down real quick it's nothing too crazy and as you can see over here we actually have the app right here very basic login username field password field a login button that's translated right here we're using storyboards that's fine whatever and then we have this dummy database this is what we're gonna pretend that we're gonna use for our database just so that we can you know check if we logged in or not what I really want to focus on though is this part right here where we actually have some of this logic right so we're actually going through a process of validating if all of this information that's being passed in is actually good right we want to make sure that the user name is going to meet some criteria and the same thing with the password if that all goes through fine then what we're going to finally do is we're gonna send that data over to our database at which point we'll be able to log in and if you know for whatever reason if we don't send the right credentials then we'll get a login error so the problem that a lot of people end up having is they don't know how to test stuff that's already inside of the view controllers because it's already tied inside of their view controllers and I would say that yes there are a lot of different ways that you can test a view controller you could even choose a different architecture that will make things a little bit easier but overall what it really comes down to is if you're working with some type of logic think to yourself now am I able to break some of this stuff out and as we look right here we're doing some type of logic where we're checking is this a valid username is this a valid password and if it is then we can proceed but if it's not then we can essentially throw some type of error and this type of code is actually the easiest to test and this is what I recommend that you try to do in your very own projects is you look for code where you're passing something in and you're getting some type of result out so whenever you see something that can go in and then it transforms or there's some type of checking or logic that's run off that information that you put in and then you get some type of output that's going to be the easiest part to start testing so what we're gonna do is we're gonna actually start refactoring some of this code into its very own service so let's go ahead and create a new file for that now alright so as you can see I created a new file called validation service and what this is going to allow us to do is break out some of the validation code that we have back in our view controller and put it in here in two separate functions from there what we'll do is we'll be able to actually test the validation logic in unit tests and that's why we want to break it out is one to make sure that we have all of our code that's related to each other and doing a specific thing in one place as well as make it very easy to test so let's start adding some functions in here that will allow us to validate the username and validate the password all right so now that we have two functions one called validate username and one called validate password what we can do is we can copy that functionality that was back in our view controller and we can add it to each of these functions where it's relevant so let's go ahead and do that now all right so as you can see we refactored some of the logic so that each validate is only validating the thing that it's supposed to right so we have the username being validated right here and we have the password being validated right here and we're getting these errors because validation error is actually part of the view controller down here so what we want to do is we'll just actually break this out into its own object and it'll just be a enum and we'll keep that in the same file as our validation service all right so now that we have validation error over here in our validation service file and it's no longer a sub object of our view controller now we can actually use it just like this very nice very clean very sexy so what we need to do is we need to head back over to our view controller we need to remove all of this code right here and we need to replace it by using our validation service in order to do that we actually need a couple of initializers so i'm going to throw in a few at the top all right so as you can see now we have a property called validation and that's just going to be our validation service and then regardless of how you decide to use your initializers whether it's going to be through programmatically creating this view controller or if you're using storyboards then we're just going to simply pass that validation object and initialize our view controller with that so now we can go down here into our DTaP login button functionality and we can refactor the rest of this code to make it nice and sexy all right perfect so as you can see very nice very clean very tight and sexy so what we want to do is we just want to make sure that all of this works and I understand that we haven't actually gone into testing right now but testing actually encourages better practices for breaking code out and making it more modular and reusable and therefore even more testable so let's go ahead and make sure that our validation logic works and I'll actually have the validation service pulled up here in the background so that you can kind of see what's going on if I have something too short or too long we're gonna get one of these errors if it's too short or too long for the password same thing so let's go ahead and check that out right now all right perfect so we went through all of our different use cases we verified that all the logic is still working as we expect it to and now we can actually start writing tests so let's go ahead go back over to our project and what we need to do is we need to actually go do file new and we're gonna actually do a new target so click on target make sure that you scroll down and you scroll to the test section what we're gonna do is we're gonna select unit testing bundle from there hit next and what you'll see is you're gonna have the product name automatically be populated as whatever your project name is in this case mine is called just start testing so you see just start testing and then without a space it's gonna say tests just so that I could keep it a little bit more consistent I'll add a space right there it really doesn't matter so from there once you have your your product name set the way that you want it to be go ahead and hit finish alright so as you can see what it did is it created a target down here and if you do the drop down you'll actually see that you get this new Swift file now we're actually not going to be using this specific one it's all boilerplate you can actually delete all of this and nothing what happened we're gonna just go ahead and create a test for our newly refactored code that is very easy for us to test so let's go ahead and create a new file and call it validation service tests when you're creating a new test file make sure that you choose unit test case class now you don't have to do this you can create it from scratch and that would be fine but this gives you all the boilerplate just like any other Swift file would whenever you're going to save a new test file make sure that you have the correct target selected in which case it's going to be a unit test and it's gonna have a little Lego block look looking icon right there make sure that's selected and that you're saving the file in the correct spot now we have our validation service tests and once again all of this is boilerplate we can just go ahead and remove it just like if it were a view controller file from here what I want to do is I want to make sure that I open up my validation service and I can see it while I'm writing my tests so that I can go back and forth make sure that I'm getting all the use cases that I want to have covered so what I'm gonna do is I'm gonna get hit command shift and oh and that's gonna give me my open quickly from here I could type in validation service as soon as it comes up I can do control shift editor and that will actually allow me to choose where I want to put this screen I'm gonna hit the right arrow key and I'm gonna put it to the right and now I can see both my tests and my validation service over here at the same time I can go ahead and close this pane by hitting command 0 and that's gonna close it so now I can see both my tests and my validation service the first thing that we need to do is get our validation service into our validation service tests class so that we can reference it so let's go ahead and add that now now what you'll notice is that you're actually not going to get any syntax highlighting and you won't actually be able to access anything from your actual project and the reason for that is because we have to do import on our actual app so what we need to do is we need to do at testable and import or app I'll do that now and just keep in mind that when you do import on your app if you have spaces in your name it'll actually show up as underscores just so that it is one long word so make sure that you have at testable and import your app name now as you can see we get syntax highlighting and we have a validation service but if I were to try to use this it would actually crash because we don't have an instance or we're not sending a value to this validation so what we need to do is we need to add a instance of this validation service whenever we're going to start a test and the way that we do that is with a method called setup so let's add that now so setup is kind of like a viewdidload you could kind of think of it like viewdidload every time you run an individual test and each function that we're gonna write below is going to be a test every time you run one of these tests this function will run and what we want to do is we want to make sure that we're going to have an instance of our validation service for each of these tests so since our validation service is a struct and it just has a couple of functions in it and we're not having any state on it or modifying it in any way we don't really have to worry about if it's going to affect our other tests but it is best practice to make sure that you tear down any state that you have in between each tests so nothing leaks over in between tests and what I mean is if this had a value of like 1 if we were storing some type of value of like 1 or whatever inside of our validation service and then we were to increment that during one of our tests then move on to the second test and we were expecting it to be 0 well it would be one from the previous test and that's why we need to implement the teardown method so as you can see I'm setting validation equal to nil now if you run into this problem it's probably because you used autocomplete and what it's doing is it's actually using the class function and that's not what we actually want so remove the word class and there's a different function called teardown on it which is an instance method so it will actually be able to access any properties that are stored in here lastly what we want to do is we want to make sure that we keep any original functionality just like whenever you override anything else like in your view controller or any other class we want to keep that functionality so make sure that you do super dot setup super dot teardown all right finally we're pretty much set up and just to make sure that everything is working as expected so far as you can see there's this little diamond where the number 12 would be for this class let's go ahead and run that and that's gonna actually run our unit tests any and all unit tests that are inside of our validation service tests and as you saw it did pass so whenever you hit this and you see the little icon pop up it'll let you know did the test pass did they succeed did they fail and what's going on since we don't have any tests it should definitely pass and it did so let's go ahead and start adding in some tests so the first thing that we're going to do is we're going to test validate username this function right here and what I like to do is I like to go the happy path first I want to make sure that everything is working as expected and then I go through each of the different types of errors that might be experienced in any of these test cases and go through each of those afterwards so happy path first and then I go through all the errors afterwards so let's go ahead and add in a new test for validate username so we have our first test which is called test is valid username and the first thing you're probably going to notice is that it's snake case and we don't normally write snake case anywhere else in our code but with tests this is a normal convention and some other languages you might even see whole entire strings being used as the name and what the general idea is is that you actually write out kind of like a sentence of what you're trying to test to make it as clear as possible so right now we're testing is valid username we're just making sure that it's working as expected so let's go ahead and test validate username and we'll pass in the working path first all right so as we already know validate username and passing in something like helo loco where the character count is greater than three and it's less than twenty meets all the criteria and we should definitely get a valid username right so this would essentially work if we had it in our code and we do have it in our code so we wouldn't expect it to throw any errors because this is a throwing method right since it throws so the way that we would test this is we would actually wrap it in an XC t assert no throw alright so as you can see we wrapped it in XC t assert no throw and if we option click on this you can actually see that this is a function that's expecting an expression in this case it's an this is our expression try validation validate username keylolo go and if it doesn't throw anything then essentially the test will pass if it does throw an error then the test will not pass and essentially this would be false so whenever you have these xct assert statements or functions you're going to pretty much be checking that whatever follows the assert word is true so no throw does it throw if no then that's true and essentially our tests will pass so let's go ahead and run this test and as you can see we have the play button right here it'll run this individual test and we get the check mark right here and it says our test passed now if we were to change this to something that wouldn't meet the criteria like KL since this is going to be shorter than three characters we should get an error and it's gonna throw that error so then it will be throwing therefore no throw is false and that means that our test will fail so as you can see our test fails and actually gives us a little message right here it says user name too short that's what the error that's the error that was thrown so this is kind of the basis behind it what we're going to do is we're going to essentially be using xct assert to determine whether something is true or not and if it's true then it passes if it's false then it fails so let's go ahead and fix this again all right going to run and it looks like everything's working as expected and what I'll do is actually close this out a little bit more just so that we can see it a little bit easier all right so the happy path works so now what are some of the situations that can cause an error to be thrown so if we go ahead and take a look at our code the first thing that we're checking is to see if user name is nil or not since we allow it to pass in an optional string we're actually checking does user name have a value and if not then throw an invalid value error so what we're gonna do is we're going to pass in nil we're going to trigger this case and we're going to verify that we do get this error if we don't get this error then we would have a problem but we want to validate that we do actually get this error so let's go ahead and create a new test that that checks that all right so as you can see what we did was we used xct assert and now what what are we asserting we're asserting that it throws an error right so when we try to validate that a username is valid but we pass in nil then we should be getting an error so let's go ahead and run this and make sure that this is also correct but right now Xcode is being kind of weird it's not you know checking to make sure that this is a valid test so you can fix this one of two ways you could either go up to the top and you could hit play and that will trigger the tests to run all again so all of your tests will run or you can do command be and that will build it it will update X code and it will verify and see that there is a new test to run and we can just hit the play button right here alright so as you can see we have another passing test but this isn't that helpful because this error isn't actually being identified what what we want to do is we want to make sure that we're getting a very specific error because if we were to pass a nil and we were to get you know an error that says user name is too long well that's incorrect information that we would be passing to the user so what we want to do is we want to actually check what error is being thrown and then what we want to do is we want to validate and verify that that error is the error that we expect so let's go ahead and explicitly say what error were expecting and let's go ahead and also hold a reference to the error that's being thrown and we can do that by opening up a closure at the end of xct assert throws error it'll pass in the error that's actually thrown we need to hang on to a reference of that and then we can check the other stuff against it all right so as you can see we used this closure and actually close this out right now we actually use this closure to capture the error that was thrown we pass it into the closure and now this error that we have outside of this closure right here is going to be ready to receive that error now this is just a generic error that's thrown in here so what we need to do is we need to cast it as a validation error this way we can compare the two later in our validation of assert equal right so as you can see we have this expected error we're expecting it to be a validation error and specifically invalid value error so now when this error is thrown and we pass it into err we should expect that the air that was thrown is the same as invalid value and we're just doing that right here our expected error is equal to this error and that's what xct assert equal is saying so let's go ahead and run this test and make sure it still passes perfect so as you can see it's very simple we're just checking very basic things and we're just going down the list of the different types of errors that we can have because each of these is a different route that our code can go down and we want to make sure that we cover all the different cases now there's two more cases that we need to check and if we open up our validation service one more time we can see that we need to check for or we need to run the tests for the username being too long and they username being too short so go ahead and pause the video right here give it a shot see if you can implement these yourself and come back and see if your code is similar to mine all right so as you can see I created these two different tests and one is testing if the username is too short the other is testing if username is too long so let's go through these real quick and as you can see the expected error for this use case is going to be user name too short we're gonna do the same thing all I'm going to do instead of passing nil is I'm gonna pass in a short username and as we might remember that if we open up our validation service you can see that it needs to be greater than three characters so if it's exactly three characters or less then this test should fill or this validation should fail thus throwing this error and it's going to be cast as a validation error then we have that error in here and we're just going to make sure that these two the expected error and our error are actually equal let's run that make sure that it works perfect and we get succeeded now going down to if the username is too long we did pretty much the same exact thing we're just making sure that the username that are the expected error is username too long now what I did differently here was I created another property called username and I passed in the username since it was going to be a little bit longer and I added this additional test now we're doing xct assert and we're just saying is it true or not and as you can see I'm saying username count is equal to 20 so once again if we were to open up our validation service I want to make sure that it's greater than 20 or that our password is uh are not our password but our username is less than 20 and if it's equal to 20 then it's not less than 20 therefore causing us to have a too long username so I'm just making sure that our username is 20 essentially the threshold that we need to hit in order to give this to throw the error and then we do everything the same once again validate this username we get a thrown error cast it as a validation error hang on to that error up here and then we check is our expected error the same as the error that we actually got we run that test and as you can see we get the assert true is passing we get this assert throws an error is passing xct assert equals is passing if we were to change any of these like if this was 10 we should get an error right here saying that this test actually failed and it would give me an error specifically right here so that's pretty much all you have to do in order to write tests and as you saw we pretty much did it by refactoring refactoring some of our code and then making it much easier to test and actually making our viewcontroller call site way cleaner a lot less lines of code so now that we actually have tests what we can do is we can edit our schema go up to the top go down to edit schema and what we're going to do is we're going to make sure that we're on the options tab of our test and we're going to do gather coverage for all targets and go ahead and close that and what that will do is it will actually allow you to know how much of your codebase is covered so when I actually run one of these tests or do command you and I start running all my tests what will happen is a report is going to be generated for us and it's actually going to tell us what how much of our code base is covered so if you go all the way to the right of the navigator pane and go to reports then what you can see is you can see this test which was done right here and you can actually see all the different tests are being logged in this in this pane as well so if you go to the most recent test and then you go to coverage and you drop down you'll actually see your app and all the all the code coverage that you actually have now you'll get percentages over here and I don't want you to strive for 100% I think that it's actually probably impossible to get 100% code coverage it would just make your code completely like unmodifiable so you want to keep your code mutable you want to be able to make changes add things to your app so don't strive for a hundred percent but you should strive for some type of number maybe fifty percent maybe higher than that maybe a little bit lower if you're just starting now now the thing that we were started the thing that we were testing was our validation service and for this specific file if we double click on it what you'll actually notice is it's showing you which areas are not actually being tested when you're creating smaller services that do a very specific thing it's perfectly fine to go for 100% coverage I'm just talking about not the entire code base but for something as simple as this something that goes through and does a very specific thing it should be very easy for you to go back and do 100% code coverage for smaller services like this for as you can see the error description is actually not being tested whatsoever so we can add in some tests or we can modify our current tests too to cover these use cases the same thing with validate password so since we already have a couple of tests that are using our username too long too short and invalid value what I can do is I can go back over to our tests and I can just add in a simple xct assert statement to each of those points and then we would actually get more coverage on our code so let's go ahead and add some of that in right now all right so as you can see I added the same assert statement to each of my my error cases so test username is nil right so since we have invalid value and we're going to be having the expected error test the error description against the actual error that we get and check that error description and this is actually wrong and we're checking that against that error description what you can see is we're actually going to be able to get more coverage on our code by having each of these error descriptions checked so once again if we go over to our coverage before we run our test one more time and we take a look our our validation service we have the entire block right here that's read let's go back or we can actually just do command you to run all of our tests we get tests succeeded and it's actually showing that our blocks are now limited to just these two these few things have not been tested by our code everything else has been tested and there's no longer that red statement right there so it's really easy to see what parts of your codebase are not being covered and to see which parts are being covered and if it's something like this then you know I need to go back and add a couple extra tests or modify my currently existing tests to cover these these specific cases so what I would recommend is go through with this project download this project and I want you to actually go through and add the the test cases for your validate password it's pretty much all the same and the benefit to doing this is actually writing it and typing it out yourself and internalizing testing so I really want you to just pause right here download the project files the link is in the description and pick up from here go back over to your tests and add 3 more tests down here that will cover the same things you want to check if your password is nil you want to check if your passwords too short if it's too long go back and add those right now alright so that's pretty much it I hope that you accepted the challenge to go over and do that work by yourself write out those tests by yourself so that you can really start internalizing how to write tests and start adding them to your project I mean you don't want to be in the tutorial trip right well if you made it this far I really appreciate it thank you so much for viewing I hope you learned something new and if there are any more topics that you want me to cover then let me know leave a comment down below and if you're not subscribed please subscribe it really helps the channel out and I'm gonna be putting out all kinds of great content so that's gonna be it for today everybody thanks again have a great rest of the day and go out there and make sure you keep coding passionately
Info
Channel: Kilo Loco
Views: 27,797
Rating: 4.9731846 out of 5
Keywords: swift, unit testing, xctest, getting started with unit test, unit test, swift unit test, swift testing, swift integration testing, how to write tests in swift, swift tests, xctassert, what are unit tests, swift refactoring, swift unit test beginner, beginner unit test, swift test view controller, xctest unit testing
Id: P-Zow2yVx4o
Channel Id: undefined
Length: 31min 14sec (1874 seconds)
Published: Wed May 20 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.