iOS Unit Test Mocking Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome back to another coach pair tutorial so you're building the next Facebook you're building the next social media superstar app and you're building out your UI you're building out your view controllers and you get to a point where your web services are not implemented yet so you can't even build out your networking layer so what do you do do you wait until the last minute of the project when all of the services are fully available and finalized to start implementing that in your code which is pretty risky or is there another way where we can mock or stub out the service responses while we develop the rest of the app and then when those services become available we can just swap out our mocks for the real ones and things should just work and it turns out there is a way to achieve that through test-driven development using concepts known as mocking and stubbing and so we're gonna break down how we can apply those principles in our app in this tutorial do all this mocking and stubbing and test-driven development is a little too advanced for you and you are looking for a great beginners course make sure you check out my iOS fundamentals course it's great for beginners it'll teach you the basics of structuring an app and getting yourself up to speed with all the fundamental skills if you sign up with my udemy link you'll get that course for 50% off if you use my coupon link so let's open up Xcode and get started all right so before we dive into the coding part of this tutorial let's set the context for the environment so starting off I already have in this project here a simple API client that makes two Networking calls to a local server that I've set up to basically mock a remote server or remote service and it's really simple I'm trying to simulate building and like a Yelp kind of application like a food reviews application so I have two endpoints I have one for requesting reviews from a particular API and then one for logging in to a profile service and that's it right so these make real networking calls over to my server and my server sends back responses that I'm expecting because I coded up the server to do that and the implementation the networking call goes through URL session which makes a data task and executes the requests just like you normally would do with a regular networking call now I have a model objects called user profile which conforms to decodable and all it does is parse back the JSON that it gets and builds out the model and I have one for a review or a food review model that's the same thing so these three classes are what I'm starting off with and they're going to be the building blocks from which we built from now in my unit tests now this is where it gets interesting I have set up some tests for my little food review API client I have a test for logging in and I have a test for retrieving food reviews now on the server side let's go ahead and take a look at how it's structured that really quick and so here's my server up and running and I'm using the server side swift perfect server to build this I have a tutorial that covers this if you want to learn how to do it I'll link a card up in the top right with a link over to that tutorial if you're interested in building that but it's really simple it has two routes it has the route for the API login that we were talking about in the client application and it has a route for the reviews now it's really simple all this does is for the reviews it returns back some JSON basically two little reviews here that I've hard coded in myself and it sends that back through the service response now for logging in there is a logic check for the username and password that I've hard coded in just that I'm expecting to come back in here and if it doesn't I send back an error in the response which is just a simple JSON error here and if successful it will return back a user profile that's just been hard-coded on the server so that's the structure of the server now if we go back over to the client application and go ahead and run our unit test because a little point of this tutorial as we want to understand how to apply mocking into our unit tests and what we can gain from that so right now all of my unit tests are running with the server being live with the server being available all the services are reachable so let's see what happens when I want to test say building out some of my models from the responses that come back and so you can see here that my test succeeded no problems there if I changed my you know log into something that is wrong so remember it's looking for those hard-coded username and passwords if I just changed this to something like a B C I expect my test should fail we can run that we can verify that and sure enough yeah that failed here because I got the error that came back from the insertion so so far everything's good but you know that in a real world scenario a lot of the times the services may not be available when the app team starts building out the app and so typically what happens is contracts will be associated or so typically what happens as contracts will be established between the app team and the web service team basically saying this is what the request looks like and this is the response that you can expect to receive right so if we look back at the server we could say that maybe I've as a you know server as a server developer have come up with some contract that says I will give you back a profile with the following attributes if you successfully login right and so as a client-side developer I know that I'm going to be getting back some kind of structure and I can use that information to maybe build some kind of parsing logic around that right so if I want to do something with the response maybe constructing my models down here or maybe doing something else now I have knowledge ahead of time even before the web services are built of what I can do and I can start building that but the question is how because obviously right now I'm running everything through a real implementation that's going through URL session and making a real networking call now if I turned off my server right and now it's not running and then I run my tests again let's see what happens and so they're running the tests and I have some expectations here that timeout after 10 seconds and I will expect that these are all going to fail because my servers down yep and sure enough we're failing here we're getting back errors so we know that that's that's not really a good way to try to build out unit tests for our networking services and all the other things that depend on them so how could we solve this problem what do we want to do well what if we could mock this API client to basically give us back what we know we're going to get before the actual implementation has been built and that's what we're gonna start doing right now so really the best place to start if you don't really know what's a mock is look at what your actual implementation is doing and let's see if we can kind of infer some information from here so we know that we have a login method and we know we have a method for requesting reviews and we know that we have another method that executes say URL requests now in a mock situation we don't care about the actual implementation of this in fact all we want to do is maybe stub a response back that is going to basically replace what we get back in a real networking call but give us the results that we want to use in our to mock for other things to test against and so the best way to come up with a strategy for this is to create a protocol a protocol that can be implemented by the real API client and a protocol that can be implemented by the mock API client and that protocol is going to consist in this instance of our request reviews method and our login method because those are the shared things that we care about mocking in in this particular class so what we can do is we can go back over to our project folder structure here and we can create a new protocol we can call this food review API clients critical and so now what we can do is we can go back over to our food review API clients and I'll just do a little split here so I can get the side-by-side and what I'm gonna do is go here and basically copy this method signature line through line and paste that into the protocol and same thing for the request reviews method here and paste that into the protocol as well and the final thing I'm gonna do is make our food reviews API clients conformed to this food reviews client protocol so I'm gonna do it an extension down at the bottom let me get back into regular view here so it's easier to read I'm good all the way down to the bottom it will create an extension off of the food review API clients that implements the food review API client protocol let me build this because this is gonna scream at me for not having be actually no it won't because I've already implemented them already so what I'm gonna do is just cut and paste these two methods into the extension just to kind of separate out the protocol logic from the glass logic here and that'll silence that there and so so far so good but now we need to go ahead and create our mock so what we can do next is go back over into our structure here and we'll go ahead and create the mock class now you want to be down in your test target and usually you have the option to create a test to target when you create a new project but just in case you don't have one if you go over into your project you can you can do this one of two ways you can do the plus icon down here and you can do plus and you want to do a unit test target which would be an iOS unit testing bundle and once you created that you'll have your app targeted here and your test target over here so what we'll do is go down to the test target and I'm just going to create any group to organize this I'm going to call that machs and inside of this group I'm going to create a new file and this is going to be the mock one right so this is our mock food review API client and it's going to belong to the test target and make sure your test target is selected you don't want this in your app target this is purely a code file that lives in the unit tests and then hit create and so in here what we want to do is there's an ant testable import for our test mocking tutorial target we do that so that way that way we can get access inside of our unit tests for all of the things in our actual app or our you know whatever target we're trying to test so once you've done that we can go ahead and create a class mock food review API client and then we'll wrap this in an extension so we'll do extension mock food review API clients which conforms to the food review API clamp protocol just like that and we're going to get some errors here until we add the stubs so we hit fix and it's going to go ahead and add the methods for us that we needed to implement based on what the protocol says so now at this point we have our methods so what do we want to do here we have a couple of options now granted that I since I designed this little web service I know what the response is now maybe you don't maybe you have a contract that says this is what you're going to get so what we can start doing is we can use this completion handler if we want to basically pass back some fake JSON from in here and then use that in the test so we can do the same thing for the request reviews so the next thing is I'm going to add a couple properties to the class here so we'll do something var should return error equals false VAR login was called goes faults and bar requests reviews was called set defaults in addition to that I'm going to create a mock error or basically something that looks like it this say mock service error and I can give it a case of login let's call it login in case request refused and I'm going to also provide a function the reset function that lets us basically turn off all these properties we have so should return error can be set back to false same thing for login was called and same thing for requests reviews was called they're almost done here and the next thing I'm going to do is create an initializer that looks pretty similar to the other API client so we can do a in it and then self thought should return will equal the value that we pass in as the parameter and just for the heck of it we can create a simple convenience initializer that just defaults to false until we actually set it manually in a test maybe so so far we have our mock almost finished we want to add a couple more things now we want to add basically the responses that we want to simulate now you don't have to do this you could you could simply if you want to determine an interaction was called you can set something like login request was called equals true and you could assert that you know this protocol forced you to call this method when you want to call it you can set this property even to assert that that's true but like I said we can also fake stuff that comes back through here because we know the signature of the service all right so I'm going to swipe on over to my server project here and I've got these side-by-side in full screen mode and I'm gonna grab I'm gonna copy this now I have the luxury of doing this because I've you know hard-coded my server but in reality you might have to come up with your own responses to send it back and what I'll do is just paste them in here is properties to the mock call this login mock login response like that I'm gonna also go back here and grab the reviews JSON from here come back over I'm just gonna rename that mock reviews response what we can do next is start sending these back in the completion handlers in here so for the login method what we can do is we can do a check if should return an error then we'll do that otherwise we'll send back a valid response so in this scenario here what we would do is call the completion handler with a nil parameter for the JSON and we can then do a what I call it mock service error dot login here and for the success case completion Handler and we can send back the mock login response and know for the handler for the error there and we can do the same thing down here we can set our property request reviews was called equals true and if should return error completion handler no mock service error dot request reviews for that particular call else everything worked test the happy path mock reviews response instead and nil for the error there and so now we're at a point where we can go back into our tests and make one simple change and start using these right away so if we go back to the test class here if I simply do that and run this let's see what happens and look at that so it was that easy I could swap out my real food review API client for the mock client and feed it right into my tests and everything works now let's say for example that I wanted to do something like this food review which is really my mock so I guess I should rename that to mock food review API client but dot should return error equals true right and because if you look at my test logic here I assert nil on the error that comes back now by me turning this flag to true it's going to actually simulate the failure path so let me go ahead and run this and this should actually fail yeah and you can see here that it's it's crashing on the assertion in here because that error came back with the value so there's a lot of things that we can start doing with this and what we can ultimately end up doing is removing the reliance on the real implementation for the services we can build all of our network services in isolation assuming that we know the contract of the API the request the response we can mock everything and we can build our app logic around that so if I needed to build some kind of core data or core data implementation other things that rely on this service well I can use the mock version of this and use it to test other things at a functional level and also to if things change throughout development right let's say endpoints change parameters change the nice thing is this guy is wired up to a protocol so if you go down here if I make a change in this protocol let's say a method name changes our new method comes one of these methods gets replaced the moment I make this change in the protocol it's going to complain in two places it's going to complain in the real food API review client and in the mock one so it kind of forces me to keep them in sync which is great so as things change I make a change in the protocol and then I have to fix it everywhere else which keeps my tests in sync with my application logic because other times if you try to test you know maybe not using a protocol based implementation you could make a change in your app forget to update that change in your unit tests and then all of a sudden you don't have complete test coverage or you have outdated tests and you're getting back basically false negatives or false positives depending on the outcome now let's say I'm trying to think ahead and really prevent a lot of bugs in my app well okay what if the server you know just doesn't send me review title for one of the responses right what's going to happen like how is my house my code going to handle that and so now that I've removed it from my response here I can go ahead and run that and verify real quick what's going to happen and I know it's going to app but let's let's see what happens right and so I right away I failed because my decodable implementation here is going to throw a fit because it's expecting to have all of these keys and I just happen to remove one so I can kind of battle test my code around a lot of these paths here which is awesome so I can I can kind of in advance say well okay you know what if the server does this and what if this happens and I can get a good feel for how to safeguard against those situations you know and any any where that I'm using this mock or stub implementation for stuffing a response back so let's try to bring this back to home base and really look at the big picture of what we covered in this tutorial this tutorial was not about writing a network client to make those service calls it was not about setting up a server it was about understanding a testing concept or a testing pattern that you can use in your code in your development to greatly improve the testability of all the code in your app increase your code coverage and reduce bugs and if you look at it we weren't really testing the network service we were really testing what happens to our model objects that get built from the response from the JSON that comes back and I could expand upon this into lots of different scenarios where I give back extra properties I take properties away and I can I can do all kinds of things to basically battle test my models under all kinds of different situations without having to even have a real networking service available for me to use as long as I know what they're going to give me then I can do whatever I want and build upon those assumptions and so that's really the way to think about this going forward and you know there's third-party libraries that you can use that can generate mock classes for you and there's quite a few of them in iOS they're pretty popular but if you want to do it yourself this is one simple way of going about doing it and that wraps up this tutorial so now hopefully you are armed with some knowledge and some ways that you can write and improve the quality of your code along the way reducing bugs increasing code coverage and overall writing better code so if you found this tutorial helpful you know what to do smash that like button and consider subscribing the code Pro to stay up to date for all the latest tutorials make sure you follow code Pro on social media you can find me on Facebook on Twitter and on patreon and let me know in the comment section down below what you guys would like to see next thank you so much for stopping by and I'll catch you in the next one [Music]
Info
Channel: Code Pro
Views: 19,188
Rating: undefined out of 5
Keywords: ios unit test, ios unit test tutorial, unit test, ios unit testing, ios unit testing framework, ios unit testing mock, ios unit testing swift, ios unit testing tutorial, unit test xcode, test driven development, unit testing, unit testing tutorial, code pro tutorial, codepro tutorial, learn ios, mock unit testing ios, stub testing tutorial, swift unit testing, what is unit testing, xcuitest tutorial, learn to code, mobile app development, mock testing, objective c
Id: zB61-7E7eoo
Channel Id: undefined
Length: 24min 50sec (1490 seconds)
Published: Sun Sep 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.