Lesson 11 of 21
Mocks And Stubs
In this demo we look at a more detailed problem, where we would need a stub (external link, opens in new tab) and a mock (external link, opens in new tab) in the same test.
In this lesson
Roy Osherove adds a requirement that a web service is called when the slow logger throws an exception, for example when the disk is full on a machine. The fake (external link, opens in new tab) logger becomes a stub (external link, opens in new tab) with a will throw method that sets the exception to simulate, and a fake web service becomes the mock object (external link, opens in new tab) that the test asserts on. He writes the add when logger throws calls web service test with an out of memory error, and says the test name (external link, opens in new tab) tells you what to do when the fakes get confusing.
Transcript
[00:03] (external link, opens in new tab) Now let's take a more complicated scenario. Now the requirement is that I have a slow logger and a web service. Now the requirement is not that just you call a slow logger but when you call a slow logger and the slow logger somehow has an error then the web service gets called. So imagine this. I am the system administrator and whenever the disk is full on the machine, I need some kind of administrative web service to be notified that there's a problem with one of the machines.
[00:42] (external link, opens in new tab) Make sense? So how do I test that web service gets called when slow logger throws an exception? Well, again, web service is something I don't have control over, but I can take control over by creating fake (external link, opens in new tab) objects. So again, I need to replace the web service with some kind of interface and the logger with an interface. I'll have a fake logger, but now I also need a fake web service. Now this time I'm going to make the fake logger throw an exception.
[01:14] (external link, opens in new tab) I need to simulate an exception for some reason, right? But I'm not going to assert against it because I already asserted it gets called in a different test. What I'm going to do is I'm going to use it as a stub (external link, opens in new tab). I'm going to make the stub throw an exception so it behaves the way I want, but I'm not going to assert against it. And then I'm going to assert that the web service got called. So in this case, the web service is the mock object (external link, opens in new tab). And the fake logger is going to be our stub.
[01:45] (external link, opens in new tab) And this is the ch these are the changes I'm going to do to the fake logger. I'm going to make it throw things, but I'm not I don't want to hard code the things it will throw. So, I'm adding a field called to throw and I'm checking whether to throw is null and if it's not, I just throw whatever is in there. And then I add a method called will throw. This is not part part of the interface, just part of fake logger. And that gives my test the ability to decide which exception I want to simulate.
[02:26] (external link, opens in new tab) And then the web service will look exactly like the fake logger used to look. Just saves the values and then saves them and make them public so that we can check what was called. And then in my test look at the name add when logger throws calls the web service. So I do create the fake logger but then I say fake logger will throw and then I give it whatever error I want
[03:00] (external link, opens in new tab) And then I create the fake web service and I create the string calculator (external link, opens in new tab). Now here I name it with twoep because it's part of the files. I wanted you to have versions of the files with all the differences, but it would be the original string out later and then I would send in the fake logger and the fake web service because I now have two dependencies. So I have added another argument to the constructor. Then I call add and I assert that I got back something into fake web service.
[03:32] (external link, opens in new tab) I might want to name it mock web service in this case. Okay. Let's see that in a let's see that in the in the real code. So let's say I want to go back to my to my tests and again I want to add the feature where are I want to add the feature that it calls a web service.
[04:02] (external link, opens in new tab) So what I'm going to do is I'm going to duplicate this and say add when logger throws calls web service. So I do need the fake logger but I want to say mocklog dot will throw new out of memory error
[04:38] (external link, opens in new tab) And I want to send the string calculator something that that I can assert against. So let me rename the mock log to stub log
[05:09] (external link, opens in new tab) And I want to send in a fake web service. I'm going to say the the fake web service mock service equals new the fake web service. And I'm going to send in the logger and the mock service here. And of course, this won't compile yet. But again, I want to finish my test first. So I that I I have all the pieces in my head lined up just right.
[05:44] (external link, opens in new tab) If I don't understand my test, I'm going to be in deep trouble because at some point, especially when you just begin, you have these fake objects and you fake that and you mock that and then you forget what it is you're trying to test. And when that happens, you look at the name of the test and that will give you a map of what it is you're trying to do. Oh, this is what I this is the act part. This is the arrange part (external link, opens in new tab)
[06:17] (external link, opens in new tab) And this is the assert part. So whenever you get confused, you look at the name of the test. it will tell you what it is you need to do if it's good enough. So I do need to assert that I got back let's say got dot out of memory error from mock service dot written
[06:57] (external link, opens in new tab) And if all that's okay if If I like it, then I go ahead and I create the web service and I create an interface called web service. Then I add the constructor argument. I add the fake stuff and then I assert against it. But it would be the exact same pattern. The only thing that I need to do that I just want to show you is that how simple it is to make something throw something.
[07:33] (external link, opens in new tab) Make it field. Then all I have to do in the write method is to say if error then throw the error. Okay, let's see. And then I have to add this of course. That's it.
[08:05] (external link, opens in new tab) Now, yes, this becomes pretty heavy when you get to multiple things, but I want you to learn this technique because for simple stuff, sometimes this is actually easier than other things. And we'll get to mocking and isolation frameworks (external link, opens in new tab) later today. Questions? Okay, in that case I want you to open the following document and don't don't forget about working in pairs (external link, opens in new tab) from now on. Switching pairs from yesterday. Open the following exercise and do the first exercise.
[08:37] (external link, opens in new tab) Okay, only feature number one. You can take five minutes and get some coffee first, of