Mocking with Mockito21:08 with Chris Ramacciotti
In the last video you saw some setup tasks for working with Mockito. So that our controller tests can focus completely on controllers, in this video we mock the behavior of our services and verify that service methods were called as expected.
Sync Your Code to the Start of this Video
git checkout -f v5
Using Git with this Workshop
See the README on Github for information on how to use Git with this repo to follow along with this workshop.
Mockito Answer Interface
All right, I'm back in our favorite controller test, and 0:01 we're ready to code these tests. 0:03 Let's start with this index test. 0:05 When the controller method is called, 0:08 three things should happen that we'll test. 0:10 First, the response code should be 200, or OK. 0:12 Second, the render view should be named favorite/flash index, and 0:15 third, the model should include whatever favorites came back from the service. 0:19 It's that third item that we'll need to take care of first, that is, 0:25 we'll need to define which favorites are returned from our mock service so 0:28 that we can check them in the model. 0:31 So, let's first define a sample list. 0:33 I'll say that List of Favorites, I'll call it favorites, 0:36 and I'll use a raised dot as list to quickly define a new list. 0:44 I've included a favorite builder to facilitate creating these objects, so 0:52 let's use that, so I'll say new FavoriteBuilder. 0:55 Let's do an import of that FavoriteBuilder. 1:01 So, let's go up here to static import, import 1:05 static_com.teamtreehouse.domain.favorite.- FavoriteBuilder. 1:10 There we go, now we don't have to use Favorite.FavoriteBuilder. 1:19 We can just use FavoriteBuilder. 1:22 Okay, I'll specify that the ID should be 1, that's 1L for 1:24 1 in the long form, and I'll say it should have the address 1:29 Chicago and it should have the place ID. 1:35 You can use any values you want here, and finally I will build. 1:40 Okay, let's add another one, I'll say new FavoriteBuilder. 1:46 We'll say 2L. 1:53 Again, use whatever values you like, and 1:56 I'll say this has the address of Omaha and it has the place ID. 1:58 We'll say omaha1, call its build method. 2:03 How about build, not bid. 2:10 Cool, we have our simple list of favorite objects. 2:13 Now here's how we'll configure a mock service to return this 2:17 list when the Find All Method is called. 2:20 Underneath that I will say Mockito.when, so 2:24 say when the service's Find All Method is called, 2:28 what I want to do is thenReturn, and 2:33 looks like I'm missing my import there, so I'll hit Option+Enter for that, 2:37 thenReturn my list of favorites just like that. 2:41 We could do a static import of Mockito as well, so 2:46 let's go find that Mockito import and do a static import of its methods. 2:50 It's right there. 2:56 Let's cut that out here and paste it here, so import static all of its methods. 2:58 What that allows us to do is, instead of using my key.win, how about just win cool. 3:06 Notice how we use a form of what's called behavior driven development here. 3:13 That is we specify that when method x is called, we want y to happen. 3:18 Now that we're done arranging, we're ready to act. 3:27 So let's do that next. 3:30 We'll do that below the second comment here. 3:32 We'll say mockMvc, .perform and 3:34 again, if you wanna grab all those static imports, you could do that quickly. 3:40 Let's go back to the weatherController test and grab those mockMvc builders and 3:44 request matchers, how about right there, just copy and paste those. 3:51 So I don't end up typing them. 3:55 Saves us a little bit of time. 3:58 Cool. nockMvc.perforn. 4:00 What? 4:02 How about .perforn a get request to /favorites. 4:03 That's the URI to which we want to make a request. 4:08 And immediately we can make those three assertions that I mentioned earlier. 4:11 We'll say first that we should expect that and expect 4:15 will expect that the status is okay dot is okay,right there. 4:23 But we don't have to stop there. 4:29 We can add another and assertion. 4:31 We can say and expect that the view's name 4:33 is slash favorite slash index. 4:39 Actually, we should not have a slash there at the beginning. 4:43 And the third assertion, let's expect that 4:47 the model Has an attribute named favorite, and 4:52 that it's value, that list of favorites. 4:57 We add the semicolon to the end here. 5:04 Let me review what I just typed here, I said let's perform a request to this 5:06 mockMvc object, and that request is a get request to /favorites Upon completing 5:10 that request let's expect that its status is okay that it is a 200 status code. 5:17 Let's expect that the view name is favorite slash index and 5:23 let's expect that the model attribute named favorite has this value. 5:27 The favorites object that is the list of favorite objects here. 5:32 The favorites list that we said the mocked service should return. 5:36 Now it's important to note here that the last assertion this one right here 5:44 checks to see that the model's list matches the one that we created above. 5:49 It does not check to see whether or not the services find 5:53 all method was actually called this is important too though. 5:57 But hey there's a way to do that in Marketo. 6:00 It's called verification. 6:02 So what's do that as the last step of assertion. 6:04 Let's verify that the services find all method was actually called. 6:08 Okay, let's run these tests and see if we're in the green, so 6:15 I'll hit Ctrl+Shift+R Okay, 6:19 and we see that our test actually failed, so let's check to see what happened. 6:25 Let me click on index should include favorites and model because it didn't 6:32 It failed here and let's scroll down to see our unit testing output. 6:36 You keep scrolling and let me pull this up here to give myself a little more room. 6:41 Let's pull this up and scroll down and here, 6:45 it says the model attribute favorite expected a list but was actually null. 6:48 Now, this is a nice heads up to me. 6:55 The model attribute, favorite if I go back to the favorite 6:57 controller what I see here is that there is no 7:01 attribute added to the model called favorite it's actually called favorites. 7:06 So I mis-named my attribute in my test. 7:11 So this is a good example. 7:15 Of how a test failure can actually indicate an error in the code that you 7:16 wrote while testing instead of the application code. 7:20 In any case it's a good example of a failed test. 7:23 So let's go back to the favorite controller test. 7:26 And update that attribute name to what it should be writs and rerun this test. 7:28 So a minute. 7:36 Hit this button right here to rerun all those tests, and 7:37 let's see if we are back in the green. 7:40 Excellent, we indeed are. 7:43 As another example of a different kind of failure, 7:46 let's see what happens when this verify step fails. 7:49 Let's say that we want to verify that the services find by ID 7:54 method is called and i'll just pass it a sample id so we want to verify whether or 8:00 not this service mock its fine by ID method was called passing the value one L. 8:05 That's one is a long value. 8:12 So let's run this again and see what our output looks like. 8:13 When the verify fails. 8:17 Okay so we do have one failure here as we expected. 8:19 Let me click on that test and scroll down to our actual failure. 8:22 Check this out. 8:28 It said wanted but not invoked so 8:30 we wanted to see the services findById method called with one, we didn't. 8:33 It was not invoked. 8:38 That is, it was not called. 8:40 However, there were other interactions with the MOC, as it says right here. 8:42 The service's Find All method was called, but 8:45 we didn't verify that it was indeed called. 8:48 Okay, we expected that because we changed it from FindAll to FindByID, 8:51 but in any case that's what a verification failure looks like. 8:57 Okay, cool, let's move on to the next test method to see what 9:03 else we can do with Mike Ito to help with our unit tests. 9:06 All right, 9:10 the next test method tests to see what happens when we save a new favorite. 9:10 Let's go back to the favorite controller and see what should happen. 9:17 Let me collapses here to give us a little more coding space. 9:20 So in the FavoriteController what happens when a new favorite is added? 9:24 Well what should happen here is that the FavoriteService's save method is called 9:29 passing the favorite object it received, and then a flash message is added And 9:33 finally, Spring redirects us to the slash favorites slash, 9:40 whatever the new favorite's ID happens to be. 9:45 It redirects us to that URI. 9:48 So for the redirect will need to make sure to mock the Services Save method so 9:52 that it sets the favorite entity's ID since that is used in the redirect. 9:58 Okay, let's go back to our tests and make this happen. 10:04 So back in FavoriteControllerTest we are going to start by arranging 10:07 the mock behavior in this method. 10:11 Now mocking the service's Save method is a bit different since it's a void method. 10:15 That is, there's nothing to return. 10:19 Above when we mocked we used the thenReturn after calling when. 10:21 It's a little different in Mockito and 10:27 another mocking frameworks when you want to mock avoid method. 10:29 What this means is that we can't use Mockito's when method like we did above. 10:34 No worries though there's another method for that it's called doAnswer. 10:38 And this is another one of those static methods that comes from the class 10:44 where we did a static import of all of its methods. 10:47 That's why I can just simply call doAnswer instead of. 10:49 Now this answer parameter in parentheses here that you're seeing in 10:55 the tool tip Is a mockito interface with the single method. 11:00 Which means that we can use a lambda. 11:05 Let's do just that. 11:08 So, I'll say invocation and 11:10 use my curly braces to include all the code that I want to run in there. 11:12 Now, you can check the teacher's notes for the docs on that interface, but 11:17 the one method called, answer, accepts a single parameter 11:21 invocation on mock object that's going to be this name right here. 11:26 This is the object from which we'll be able to access any arguments that will get 11:32 passed in. 11:36 Passed in from where you may ask. 11:37 Well, passed in from the eventual call to the Services save method 11:39 so let me open the service favorite service. 11:44 Let's remind ourselves what we're mocking we are mocking this method 11:49 the save method is called bypassing a favorite. 11:53 Object. 11:57 So, we can access the object that was passed in as an argument to the save 11:58 method by going through the invocation parameter that is received. 12:03 So, we know this save method has one and only one argument. 12:11 And that will be a favorite object. 12:15 So to capture that we can do as follows. 12:16 So I'll say favorite, I'll just call it f. 12:20 We'll cast it to a favorite and we'll say invocation.getArguments. 12:23 And it's at position zero. 12:30 invocation.getArguments will give you an array of objects there. 12:33 Let's cast that object to a favorite, since we know when the service is called, 12:38 it will be as I showed you before, it will be a favorite object. 12:42 Cool Now the only task we need to perform with this object F is set it's ID 12:47 since the controller will use the object's newly set id to construct the redirect. 12:53 So let's just set it to one. 12:58 So i'll say F dot set id and set it to one in long form no. 13:00 setId(1L), and return null here. 13:06 Now I'm returning null because the interface method requires a return 13:11 statement even though the method we're stubbing has no return type. 13:14 Makita will be just fine with this. 13:17 To finish our mock, 13:20 we'll need to configure when this answer should be given. 13:21 And when is that? 13:25 We'll say when this service, when its save method is 13:27 called on any favorite object favorite.class. 13:32 Now, we could have been more specific with our 13:36 favorite object than just any old favorite here, 13:41 but this will suffice for our purposes. 13:46 Again, we just wanna make sure the controller responds appropriately 13:50 within the tight constraints of this unit test. 13:53 And finally for this test, let's act. 13:57 So I'll do that under this next comment here. 14:01 This will involve performing a post requests on that mockMvc object, 14:04 mockMvc.perform in here will perform a POST request. 14:09 I'm going to split this up into multiple lines 14:15 will say we want to post to the / Favorites URI, 14:18 if you want to reminder of what you need to be doing when adding here or 14:23 doing anything with a controller you can go back to the controller under test, in 14:26 this case when we post to slash favorites that's when the add method is called. 14:30 You see the path? 14:35 That this controller method has a mapping to a /favorites and the method is post. 14:37 So in order to test this method, we'll need to post to the /favorites URI. 14:41 Okay, so we post to /favorites and we'll need to pass in the parameters, 14:47 this is like simulating what a user would submit on a web form. 14:51 So .param And we'll say the formatted 14:55 address was listed as Chicago Illinois and 15:02 we'll add another param .param and 15:07 we'll say the placeId is set to the windycity. 15:11 Cool and you can pass whatever parameter values you want since we're 15:20 not testing the details of the favored object that is saved. 15:24 Okay now let's assert. 15:27 So here we will expect and 15:29 expect that the read directed URL is going 15:33 to be /favorite /1 and we expect that because 15:38 in the mocked behavior of the services save method we set the ID to one. 15:44 If you had set this to two you would then go down here and 15:49 test that /favorites/2 is the redirected URI. 15:53 I'll change mine back to one. 15:59 Finally, if you'd like to verify that the services save method was was indeed 16:03 called, you can certainly do that. 16:08 So, let's verify that the services save method was called and 16:10 again, you could say, on any old favorite 16:14 And with that in place, let's run our tests again. 16:21 Ctrl+Shift+R, and let's see if we're still in the green. 16:25 Look at that, love to see that green. 16:31 Time for our last test in this class, testing an expected error. 16:34 Let me collapse this. 16:38 And scroll to our last method here. 16:39 You've seen before how we can expect an exception to be thrown in JUnit. 16:43 I could say that we expect a FavoriteNotFoundException. 16:47 Now the problem with this approach is in our application I've included 16:55 an exception handler controller method that handles the thrown exception. 17:00 Let me show you. 17:03 Let's go back to the FavoriteController, 17:04 and I'll scroll down to the method called notFound, right here. 17:06 Notice that it's been annotated with this exceptionHandler annotation. 17:13 And the exception that it handles is the FavoriteNotFoundException. 17:16 The spring handles this like a try catch only that when the exception is thrown 17:20 from the detail method, that is this detail method up here, 17:25 when the exception is thrown here this method executes. 17:31 And this method executes not when any old exception is thrown but 17:36 only when the FavoriteNotFoundException is thrown, so, 17:40 this method only handles the FavoriteNotFoundException. 17:43 The end result here, is that the MVC framework catches the exceptions, so, 17:49 that it never reaches our unit test. 17:52 So, what can we do to detect this, well notice that the not found method adds 17:54 the exception to the model using that ex, just abbreviation of exception here. 17:59 It adds the actual exception that was thrown 18:05 to the model using the attribute name be ex.. 18:08 So what we'll do in our test is check the model for this ex attribute and 18:13 expect that it's an instance of the favorite not found exception class. 18:18 So let's go back to the test. 18:22 Again, since this exception right here will be caught by spring, 18:26 our unit test will never know it happened. 18:30 So let's delete that done. 18:32 We'll need to mark the service so that it throws the FavoriteNotFound Exception 18:37 when we request a certain favorite. 18:40 So how do we do that? 18:42 Well we say that when the services findByID method 18:43 is called Passing it, let's say the id 1. 18:48 Then here's something else we can do instead of saying then return, 18:52 we can say then throw. 18:57 Throw what? 18:59 How about a favorite not found exception. 19:00 And that takes care of our arrange step. 19:05 So now let's act. 19:07 So do the mock MVC object we will 19:10 perform a GET request to slash favorite slash one. 19:15 Notice that I'm using one as the ID again 19:24 Because that's how I mark my service method. 19:27 Okay let's assert so I'll go down here and all say and expect and expect what? 19:29 And expect that the view name is error if you go back to favorite 19:36 controller that's the name of the view that I use when this exception is Caught. 19:41 Cool. So, that the view name is error and that, 19:47 so andExpect, that the model contains an attribute 19:51 named ex, which is what? 19:58 Which is an instance of a FavoriteNotFoundException. 20:00 So, what we can do is use another Matchers here matchers and 20:03 this comes from the ham crest library matchers dot instance of and 20:10 I can say favorite not found exception just like that. 20:18 And again, if we want to verify that the find by ID method was called we can do 20:25 just that. 20:29 So let's do that, verify that the services 20:30 find by ID method was called on 1L. 20:35 Okay, one final time in this test class, let's run, Ctrl+Shift+R. 20:40 And we get green. 20:49 Now, if you want to test the /favorite/ID URI, 20:51 that serves the favorites detail page, you have a couple more services to mock. 20:54 I'll leave this to you for experimentation, 20:59 be sure to check the teacher's notes for more info on mocking with Mike Ito. 21:02 Next, we'll test our services. 21:05
You need to sign up for Treehouse in order to download course files.Sign up