Angular - Mocking the API

Angular - Mocking the API


Angular - Mocking the API

You may think your application is working, but if the unit testing doesn’t pass, you’ve got nothing. Turn your red lights green by mocking the HTTP Client.

Source code available at: https://github.com/JasperKent/mocking
Server available at: https://github.com/JasperKent/Restful


Content

6.72 -> Last time then we introduced this idea of full  stack development. So we had a back-end server  
11.6 -> written in C#/.NET connecting to an SQLite  database and presenting an API which then our  
17.76 -> front end - written in Angular - could get hold  of the data from and display it and manipulate  
22.32 -> it in whatever way we chose. And so we can see  all that working. There we've got the actual  
27.12 -> Angular app. So we've got our list of all  the individual ratings - so each book can  
33.12 -> have multiple ratings - and then we've got the  summary that just takes the average of the ratings  
39.04 -> for each book. We also had this ability to add a  review, so I could give ‘Dr No’ yet another review  
48.72 -> and we see that listed in there. And then  that's all working through the back end,  
52.8 -> so we can just see that here with the in-process  server. So that's just showing us that's running,  
57.44 -> no need to look at the details of that. So it's  all working in its very basic way, but there  
62.64 -> was a fundamental problem that I completely hid  away and didn't mention, which is the fact that,  
69.04 -> although the actual application is working,  the unit tests are now going to be failing  
73.68 -> completely. So if I just bring up VSCode, you  can see there we've got the application running  
78.48 -> - no problems - but if I go to a different window  and just type in ‘ng test’ to do the unit testing  
87.36 -> then we can see we're getting four failures.  And if we just bring in the browser we can  
92.64 -> see that the basic thing it's objecting to is  that it cannot inject this ‘httpClient’. So  
98.88 -> that's happening again and again and again - that  HttpClient is missing. And that's really exactly  
104.72 -> the sort of thing you'd expect, because remember  what we did last time in introducing this idea,  
109.6 -> we had our BookReviewRepositoryService and  that requires to be injected this thing called  
117.12 -> ‘HttpClient’ and that we configured for the  actual running system. But we didn't configure  
122.4 -> that for the test system, and so not only does the  BookReviewRepositoryService test fail - because  
130.56 -> when it tries to create the object it's not  got the thing that we need to inject - but  
135.84 -> anything that depends on that also fails. So for  example the all-reviews-component-spec.ts, because  
142.24 -> that depends in its constructor  on the BookReviewRepositoryService  
147.2 -> and the BookReviewRepositoryService depends on,  as we just saw, HttpClient. That's why everything  
152.64 -> starts to break down. And the only one that’s not  going to have a problem is going to actually be  
156 -> our BookReviewSummaryService because although  that does require the BookReviewRepositoryService  
162.88 -> to be injected, actually if we look at the  tests, that's the one where we were mocking  
169.2 -> the BookReviewRepositoryService, so that's where  we create the mock and of course the mock doesn't  
173.92 -> depend on HttpClient. So this bit works okay.  So what we need to do is mock the HttpClient.  
180.88 -> One thing you'll also notice actually, we don't  really need to do very much with it yet because  
184.32 -> all of these tests that are failing are really  simply failing on their first auto-generated test  
191.6 -> ‘it (‘should create’)’ so we're not really testing  that they're interacting with the HttpClient  
196.08 -> correctly. But in order to get them just to  be created we've got to have this mock idea.  
200.64 -> But thankfully we don't have to do really very  much work for ourselves, unlike when we were  
205.28 -> mocking one of our own classes. So as we saw  here where we had the summary service tests,  
211.36 -> we had to use this ‘jasmine.createSpyObject’ and  all of that work that we looked at in the earlier  
216.56 -> video. Because HttpClient is so widely used that  is actually predefined for us. So all we've got  
223.44 -> to do is include this predefined idea, so we'll go  into each of these and all we need to do is in the  
230.72 -> imports on the test we need to add an additional  import. So in addition to whatever else is there,  
237.2 -> I’ve got to put in this ‘HttpClientTestingModule’.  Once again, like we saw with httpClient itself,  
246.88 -> for some reason Visual Studio Code is not  good at importing this for you, so what we'll  
252.32 -> have to do is just say ‘import’ and then the  name of the item and then 'from' and then it's  
262.857 -> ‘@angular/common/http/testing’. And so now you can  see that's all happy and really for that one test  
276.08 -> that's all we've got to do. So you can see down  the bottom we've got four tests failing at the  
279.84 -> moment. If I just save that it'll build and run  the tests again and that's gone down to three. So  
285.28 -> that much has worked in the one place we put it.  So let's now put it in the other ones. So we'll  
290.64 -> just grab that and then we'll go to the next one,  which is the all-reviews-component.spec and hasn't  
296.8 -> got any imports at the moment, so we'll just  pop that in. We don't need the FormsModule here  
302.16 -> and we should see once you've managed to get  Visual Studio to understand where it is once, then  
306.8 -> actually it starts doing things automatically.  So that's the one we want to put into there.  
311.84 -> Then also we'll need this in the summary  component, and again get hold of the import,  
319.12 -> and then lastly in the repository service tests  themselves, that's where we've got to put that in.  
324.16 -> We haven't got anything at all in there, but in  there's where it goes; get hold of it. And now if  
329.92 -> we save all of those and we can see that's working  because we've now configured it so that it injects  
335.84 -> that HttpClientTestingModule into any situation  where an HttpClient is required. And so we're  
342.72 -> effectively mocking it. So that's got the code  working to the extent that we passed the tests,  
348.08 -> but we still haven't got any really sensible  tests. So let's actually do that. And I won't do  
353.04 -> all of them - you should be thorough when you're  doing testing in a professional application,  
357.12 -> but here we'll just do a couple of examples.  And so the first one we'll do, let's do it on  
361.44 -> the AllReviewsComponent. So at the moment again  we've just got that ‘it (‘should create’)’ just to  
368.4 -> basically make sure we can create the component.  But we want to make sure that this now correctly  
374.72 -> gets hold of the list of all of the books, and  once again we want to do that with mock data.  
381.12 -> And we've already done a bit of this, so we can  steal the mock data from somewhere else. So if we  
386.08 -> go into the summary service spec – here remember,  we had already created some mock data - so let's  
396.48 -> grab that and then put that same mock data into  our all reviews test code. So pop that in at the  
405.68 -> top there, get hold of the BookReview import and  then what I’m going to do is add another test. So  
415.28 -> typically easiest just to copy and paste  these and it should get the correct reviews,  
426.32 -> okay? So the component should have that and then  remember these are stored on the component. So  
431.52 -> what I can do is say ‘expect’ and then I’m going  to say ‘component.reviews’ so that's our list  
439.52 -> of reviews that will eventually come through from  the mock HTTP. And the first thing I’m going to do  
444.8 -> is just a ‘toHaveSize’ and then ‘3’ - so that's a  nice easy way just to check that we can see we've  
453.2 -> got the three mock reviews here. So that'll test  for that. And then let's do individual tests, so  
458.56 -> we're going to say ‘expect(component.reviews[0])’  and then we'll say ‘toEqual’, and then basically  
471.52 -> it's just got to equal what we had there,  so let's just steal that ‘book-1’ rating 5,  
478 -> pop that in there, tidy it up a little because  it doesn't really need to be on multiple lines,  
484.56 -> and that's that one. And then we're going to be  thorough, so we'll test all three of those. So 0  
491.04 -> and 1 and 2, and again if you look up here the  second one was ‘book-1’ with a rating of 1,  
498.8 -> third one was ‘book-2’ with a rating of 4.  
502.4 -> So that looks like it should be our  test, but if we save that one and run it,  
507.6 -> then again we're getting a failure. If we look at  the browser we can see it's got an empty array;  
514.16 -> it's expecting it to have size three. So three was  what we wanted, but we're not getting anything and  
518.64 -> that's simply because we haven't actually hooked  this up to get those mock reviews in through  
525.36 -> the mock HttpClient. So that's where we need to do  a little bit more work. So what we're going to do  
530.72 -> now is here we're going to say ‘let’ and then  we'll call this ‘httpTestingController’ - you  
540.8 -> can call it what you like, but that's the  normal name because that basically is the type  
545.04 -> that it is - so ‘HttpTestingController’ there,  which we should be able to do an import of. So  
551.68 -> that's coming in from the same place. So this  gives us the ability to configure and set up  
557.52 -> our mock HttpClient. Having declared it we  then need to create it, and so we do that  
565.12 -> just here after we've generated ‘component’. So  we're going to say that ‘=’ and then we can say  
572.069 -> ‘testBed.inject’ and then we just have to give  it the type, which remember was that same thing  
580 -> again, but with a capital ‘H’ because it's a type.  And so that has given us this object that we can  
587.76 -> now configure. And then the last thing we have to  do is we have to tell it to expect a GET request  
595.76 -> to the URL and then tell it what to  do when it gets back. So remember,  
599.68 -> if we look at the actual repository we can see  that in the constructor - calls ‘loadReviews’  
605.36 -> and that is where we make our request back to  the server, or back to the server if we've got  
610.16 -> this configured for the real application; to our  mock HttpClient if not. And what we need to do  
615.44 -> is check that it's gone to the right URL, that  it's a GET operation, rather than a POST - we'll look  
621.6 -> at the POST later on - and then when it does that,  we've got to make sure that we just give back  
627.12 -> as a result that mock data that we were just  looking at. So let's do that. So what we do at  
633.76 -> the beginning of this test, before we actually do  all of that checking, we're going to say ‘const  
639.76 -> request =’ and then on our  ‘httpTestingController’.  
645.52 -> We're going to say ‘expectOne’ - so we're going  to say you should get a single call - and then  
653.28 -> we have to specify the precise rules of what this  request is going to be. So let's just do this with  
661.76 -> an arrow function. So I’ll just call this ‘data’,  but then what we can do with that is we can see  
667.84 -> the various bits of data we can check.  So if I say ‘data.url’ that's going to be  
675.12 -> the URL that we're expecting should be passed  in, and we can obviously just steal that from  
680.64 -> here. So if I just grab hold of that - bear  in mind, as I mentioned last time, not really  
684.8 -> a good idea to have that hard coded because  ‘localhost:5001’ is only during development,  
690 -> but we'll look at that later, how we can get  configuration data in there. But we're going  
693.68 -> to check that the URL is what we expect it to  be, and we're going to check that the method  
701.68 -> is ‘GET’ because, remember, that's what we  said we wanted. So that will verify that that  
708.64 -> request comes through, comes through only once,  matches that, and if everything's matched that  
712.32 -> gives a green light - otherwise a red light.  The other thing we've got to do, remember,  
716.08 -> is we've now got to return the mock data. So I now  say ‘request.flush’ and this is what will actually  
725.36 -> kick off the whole process. So before that the  request won't actually be processed until you do  
730.08 -> the ‘flush’. And then we can pass in there the  data that we want to send back, which was our  
734.8 -> ‘mockReviews’, remember, that we already pasted  in from elsewhere. So doing all of that means  
741.92 -> that when we do the flush it will send the data  back. That all happens in the constructor which  
746.72 -> is actually really happening ‘beforeEach’ but it  doesn't matter. Once we've done the flush then  
750.56 -> the data will be correct and then all that should  be filled in. So all we have to do now: save that  
757.04 -> and we can see we've got eight successes. And  if we just look at the browser – remember, it's  
764.32 -> ‘should get the correct reviews’ - and  we should be able to see we've got there,  
768.08 -> ''should get the correct reviews'. So that's worked  correctly with the mock data. If we got anything  
773.76 -> wrong about that, so if, for example, I were to  change the code here so it makes an incorrect  
782.56 -> request - so let's just put an ‘s’ on the end  of that and save it we'll - see we get a failure  
788.32 -> and the failure is 'expected one matching request  for criteria' and then it was 'Match by function'.  
794.8 -> But we know what was in the function and we can  see it's not matching. So we can be very precise  
799.68 -> and get that to fail if there was really  anything going wrong with all of that.  
806.88 -> Let's just quickly do one more, because that  was looking at the GET, when we're trying  
810.64 -> to make sure the data comes back. The other  example we had was for our POST. So remember  
815.04 -> in our add-review-component,  when we click ‘addReview’  
820 -> that calls ‘addReview’ on the repository, that  then now does a POST method to the book review for  
826.16 -> all of that to work. And so that's what we need  to do in the add-review-component.spec - similar  
833.76 -> sort of thing. So in fact, let's copy all of  that. So if we get hold of our all review spec,  
839.84 -> get hold of that and put that into our  add-review-component in the same sort of place,  
845.2 -> that will mean we need to get hold of that. Then  also we need to get hold of the actual object for  
852.32 -> that. So remember that was that bit of code there  and in ‘beforeEach’. So in our ‘beforeEach’ we'll put  
857.6 -> that one in there and then we need to actually  write our test. So again let's copy that one  
865.36 -> and 'do a POST when review is added'. So let's then  set up the basic functionality. So in order to  
878.16 -> add a review, remember the way this works, we  need to say ‘component.title’ so this, if we  
885.76 -> just look at the code here, is our ‘addReview’  we've just got these two fields ‘title’ and  
892.16 -> ‘rating’. So that's what we're setting up in a  test, rather than typing them into the GUI. So  
897.12 -> we've got those. So we're going to say  ‘component.title =’ and let's make that  
901.92 -> one – well, it doesn't matter at all – ‘book-a’  and then ‘component.rating =’ give that a ‘4’  
916.16 -> and then we're going to say  ‘component.addReview()’.  
921.92 -> And the way that works - addReview  takes no parameters, and if we just  
925.04 -> look inside that you can see it picks up the  ‘title’ and the ‘rating’ that I’ve just set up,  
930.16 -> and then sends it off to the addReview in the  repository service. So very similar to what  
935.52 -> you do in manual testing. You fill in one  of the fields, you fill in the other field,  
939.12 -> you press the button. Basically, in code, the  same sort of thing that's happening there,  
943.04 -> but of course notice we haven't even checked  for anything. We've got no 'expect' statement,  
947.6 -> so it's not as yet a particularly good test.  And in fact in terms of the behaviour, all that  
953.76 -> behaviour is really hidden away. All we can do to  test it is make sure that the correct calls are  
959.92 -> made on the HttpClient. And again we do that in  a not dissimilar way. So what I’ve now got to do  
965.6 -> is, after I’ve done all, that I’m going to once  again say ‘const request = httpTestingController’  
974.56 -> and again we want only one of these calls to  be made, 'data' and then the arrow and then we  
982.32 -> now want to make sure that one's correct. Well,  the URL is going to be exactly the same, because  
990.16 -> it's still going back to the same URL on the  server. Because remember, this is a RESTful API  
996.88 -> and therefore we have the same URL but a different  method when we want to do the different things.  
1001.76 -> So if I just copy that and pop that in there,  so we want to make sure that that's correct,  
1009.52 -> but then what we also want to do is make sure  that this one is a POST, so ‘&& data.method ===  
1022.8 -> ‘POST’’. So that's checked the request. What  can then do is we can then do an ‘expect’  
1028.96 -> and then we say ‘request’ and then slightly  confusingly we say ‘request.request.body’.  
1036.48 -> And so what I want to do is make sure that the  body of this is correct - it's sending off the  
1041.76 -> correct data. So I’m going to do a ‘.toEqual’ and  then what it should be sending off is, obviously,  
1047.36 -> a BookReview for 'book-a' with a rating of  ‘4’. So we can put in title of ‘book-a’  
1056.88 -> and rating of ‘4’. And then once again we're going  to have to do this ‘request.flush’. Remember, that  
1068.16 -> gives us the data that we're returning. Well on  a POST request, remember, what gets returned is  
1073.44 -> the id of the newly created object. And we don't  really care about that. We know it's a number,  
1077.84 -> so I’ll just put ‘1’ in there, we're not going  to worry too much. But having done all that,  
1081.76 -> that should now work correctly. So if I save that,  then we've got an error. Anyone notice what I did?  
1092.88 -> It's because I put ‘4’ as a string in  there, not as a number. Try that one again  
1100.72 -> and you can see we've got nine successes.  So typical sort of thing you can make as  
1104.48 -> a mistake. Easy enough to fix. So that's it. We  have now seen how we can complete that process of  
1111.44 -> connecting up the Angular front end to the .NET backend. Not only having the application work,  
1116.72 -> but really just as importantly having the unit  tests work. And so we saw we needed to mock the  
1121.44 -> httpClient, just to get it working at all. And  then we could use that to take precise control  
1126.64 -> of our tests. So hope you enjoyed that. If you  did, do click like. Do subscribe. And next time  
1131.52 -> we'll be looking at some other aspects of how we  communicate between Angular and backend server.

Source: https://www.youtube.com/watch?v=fODLFJ8D4VU