Lesson 12 of 21

Isolation (Mocking) Frameworks

Duration: 19:53 Watch on YouTube (external link, opens in new tab)

Markdown version

In this talk we learn about Isolation frameworks (external link, opens in new tab) and how they try to help us avoid some of the problems that hand written mocks and stubs (external link, opens in new tab) can create. Later we will also see sometimes Isolation frameworks hurt more than they help.

In this lesson

Roy Osherove explains that isolation frameworks (external link, opens in new tab), which he prefers not to call mocking frameworks, automate handwritten fakes (external link, opens in new tab): they create fakes (external link, opens in new tab), save call arguments for verification and simulate exceptions or return values (external link, opens in new tab). He lists EasyMock, jMock, PowerMock (external link, opens in new tab), JMockit and Mockito, and uses jMock's expectations and EasyMock's record and replay to show how strict fakes, which fail on unexpected calls, break tests for the wrong reason. He recommends EasyMock's nice mocks (external link, opens in new tab) and shows Mockito, which is non-strict by default, with verify, doThrow and parameter matchers such as any string.

Transcript

3,014 words

[00:08] (external link, opens in new tab) Up until now we've done things in a very manual way and it was pretty easy but things become slowly problematic as you get more and more code. So when you do handwritten mock (external link, opens in new tab) objects and stubs (external link, opens in new tab) at the beginning it's pretty easy but as it gets as you get for example more parameters more method calls more methods per interface things become very very unmaintainable very quickly and you would end up with at some point with a custom library that you would have built that saves values and allows you to assert against them because you would end up with it because you would keep using these fakes (external link, opens in new tab) over and over and you would just have these helper methods become more and more generic.

[00:55] (external link, opens in new tab) And this is basically the problem that isolation frameworks (external link, opens in new tab) try to solve. Now when I say isolation frameworks I mean what many people call mocking frameworks but I don't like to call them mocking frameworks because we know that the word mock (external link, opens in new tab) means something specific and a lot of times you would use these frameworks also to create stubs. So the word mocking framework is actually very confusing and it also could lead to everyone using mock objects instead of using stubs.

[01:30] (external link, opens in new tab) And you should have less mock objects and stubs in your code. If you're going to if you're going to use them, you might as well have less than more. So I call them isolation frameworks because they isolate the unit of work (external link, opens in new tab) from the dependencies that it cannot control. In our examples, we isolated string calculator (external link, opens in new tab) from web service and logger. And what these frameworks really allow us to do is to do automatically what we just did in a handwritten way.

[02:04] (external link, opens in new tab) They allow us to create fakes without having to create a class for those fakes. They allow us to save the call arguments from those fakes and then assert against them later and verify that they were called. They allow us to build in to have a built-in way to simulate things like exceptions and custom return values because tomorrow maybe what we'll do is instead of return an exception from logger maybe what we'll want to do is to return from a method a negative number or a string or simulate anything back into our our application

[02:38] (external link, opens in new tab) And isolation frameworks provide usually very easy way to accomplish that. So they have two APIs. They have API for arranging how the behavior of the fake object will be and they have an API for verifying whether the mock object was called. Now isolation frameworks have been around for a long time and that means that they also ha in the in that in the last I would say six years we've the industry has been learning a lot about how to properly use mock objects and stubs and we have we've learned some practices of things that we should and shouldn't do.

[03:15] (external link, opens in new tab) For example, having multiple mock objects in the same test is not a great idea because that's basically multiple asserts (external link, opens in new tab) on different requirements and if one of them fails, the others never execute. But the older isolation frameworks, some of them still do not support a lot of these values and they allow you to do very easily things that will hurt you and and in terms of maintainability (external link, opens in new tab) and readability (external link, opens in new tab). What I'm going to do now, I'm going to show you some examples of how the APIs of these frameworks exist work.

[03:47] (external link, opens in new tab) I'll talk a little bit about the model of how they they expect you to use them and pros and cons about those models. The the things we'll talk about during this course, not all of them today, is EasyMock, jMock, PowerMock, JMockit and Mockito. Specifically today I'm going to show you EasyMock jMock and Mockito and tomorrow we'll also focus on JMockit and PowerMock.

[04:22] (external link, opens in new tab) And there's a difference between those two frameworks and the other ones in that they are called unconstrained frameworks (external link, opens in new tab). They allow you to fake things that you usually wouldn't be able to do if you just write your own code and inherit from an interface. For example, static methods. We'll start touching on faking static methods (external link, opens in new tab) tomorrow with the framework. But today, if time allows, we'll also get to a point where we learn how to refactor code that has static methods so that we don't even have to use these frameworks unless we have to.

[04:55] (external link, opens in new tab) So that means that in this course, we're going to learn two different ways to accomplish each thing either through refactoring or through a framework. It will give us choice and will also allow us to understand either what those frameworks are doing or the trade-offs that we have between readability, maintainability and ease of use of testability (external link, opens in new tab) of the code. So first I want to introduce jMock. jMock is basically I think it's the oldest isolation framework.

[05:28] (external link, opens in new tab) To use jMock you would create an object called mockery which I find kind of hilarious and and then you would ask the mockery object to mock a logger.class class. And notice that there isn't a word called stub. We just say mock. So even if you're going to use it as a stub, it's going to be called the mock object, which already is confusing. And one of the things that I don't like about the old style of these isolation frameworks is that a lot of them have the word mock everywhere, and they don't allow you to say whether something is a stub or not.

[06:03] (external link, opens in new tab) And a lot of them also by definition are what I call strict. Well, not me, but a lot of other people. So, what does a strict framework mean? Here's an example of a very simple test. There is no assert here yet because I haven't explained how to work with the framework. But I want you to understand what the difference between a strict and a nonstrict isolation framework means because a lot of the old ones are very strict and a lot of the new ones are non-strict (external link, opens in new tab).

[06:34] (external link, opens in new tab) And we want nonstrict. Okay? We want relaxed frameworks. So what does a strict framework mean? It means that if I create a fake object of the of of the logger interface (external link, opens in new tab) and somewhere in the add method I call a method on logger and I didn't specify exactly that this method would will be called or might be called somewhere above this test will actually fail.

[07:07] (external link, opens in new tab) I will get an exception even though I didn't assert on anything that says you called an unexpected method on the logger. Okay, I will actually get an exception right here because I I didn't tell the isolation framework what's going to happen to the mock object. Now, it used to be that this was like a pretty good practice. Nobody realized this is actually hurting our tests.

[07:37] (external link, opens in new tab) But in reality, this is what I have to do to make this test pass. I have to to to create these three lines called context checking new expectations. I say I'm allowing the log to be called with write with any string. And now this test will not fail. I haven't yet asserted on anything. But I have to do this for this test not to fail at the very least.

[08:09] (external link, opens in new tab) Now, that doesn't look like a pretty bad thing so far, but I want you to think about what's going to happen next month when I work on something in the code that has nothing to do with this feature of calling the logger and I need to add some more logging or I call a different method on the logger that doesn't do logging. it initializes something or anything really. And suddenly I have maybe three, four or 50 tests that have a fake logger, not even a mock object.

[08:44] (external link, opens in new tab) And all those 50s tests start screaming at me. You didn't tell me this method is going to be called. Now, that has nothing to do with the fact that the features that they originally test actually work. I'm just adding an extra call. Or you know what? Maybe I'm optimizing and I'm removing an extra call. In either case, those tests will actually stop working even though I didn't even assert that those mock objects or those fake object will get called because by definition in those in this framework, every fake object is a mock object.

[09:19] (external link, opens in new tab) Okay, I have to specifically say allowing for this to start behaving like a stub. And a stub is something that makes happy noises. It just allows me to run the test and test something else. It doesn't cause problems unless I specifically tell it to cause problems. That means that for every new call or anything, I have to go back to all the tests that use that fake object and add a line to allow that call to happen, which makes my test basically cement.

[09:52] (external link, opens in new tab) Every time I have to change something in production code, I'm going to have tests failing telling me that the mock objects were not configured correctly. So the test will fail for the wrong reason. And that's where strict is actually bad for you. That's why I wouldn't recommend working with a jMock. Even though it's one of the oldest and has a nice API, the fact that it is strict by default is a problem. Now how would I verify just in case?

[10:22] (external link, opens in new tab) I would change allowing to at least one. Now notice that this that this is basically my test for verifying that the logger was called. There is no assert at the bottom and the bang here will not occur. By the way, this what what's going to actually happen is that at the end of the test, there will be a hidden assert running inside jMock that will verify that this was called at least once.

[10:55] (external link, opens in new tab) But this breaks the structure of arrange act assert. Here the arrange and the assert are in the same location, which means that the test is less readable. And that's also something that's very common in the old style of frameworks, isolation frameworks, is that they don't allow you to very easily obey the idea of arrange act assert. So the tests are less readable and usually very very chatty

[11:30] (external link, opens in new tab) So the idea of allowing versus expecting is what makes me have a stub versus a mock. But I still have to have that extra line. Let's talk about EasyMock. Easy is a framework that's also pretty old. And this framework has a very interesting API. It it uses what's what is called record and replay. And the idea is that for any object that you create, for example, you say create mock of this logger. Of course, this could be a stub.

[12:02] (external link, opens in new tab) Before you actually if you want to set any expectations on the object, you record those expectations at the arrange part (external link, opens in new tab) and then you call replay on that object and from that moment on that object will either return the values you told it to return or it will expect the values you told it to expect. So in this case this is an example of the same strict by default. There is no assert yet. I didn't record anything. I still have to call replay which is just an one extra line that I have to do.

[12:34] (external link, opens in new tab) So it looks weird. And create mock by default creates a strict mock. So this line will fail as well because somewhere in here I'm going to call the logger and I'm going to get an exception that says hey you didn't tell me that that the logger was going to get called. Sorry. So to do to actually change it, what I would have to do is to use a method called create nice mock. Okay, that's the mock that you want or the fake object that you want.

[13:07] (external link, opens in new tab) That's the he >> sorry. >> This is the still EasyMock API. >> It has a create mock and create nice mock. We want create nice mock. If you're going to have to use EasyMock, create nice mocks. Okay, those are the ones that act like hippies. you know they say oh you can call me as many times as you want dude I don't care but at the end of the day we can actually ask whether something was called and it will tell us so nice mocks allow us to have more maintainable test because if I add or remove calls tomorrow as long as the feature still works I will not have to change the test it will not break

[13:44] (external link, opens in new tab) Okay I want to show you what things look like with Mockito hold on. Mockito. Okay, so here is the same test with Mockito. Now, what I like about Mockito is that it's what I call one of the new frameworks.

[14:17] (external link, opens in new tab) And by one of the nice features about the new framework is that they usually obey the idea of non strict by default very easily. So unfortunately in Mockito everything is still called a mock which sucks. I would rather have the word fake here. But nevertheless if I want to have a fake logger I would just say mock logger class. I would call it. And if that's all I did, if I was just on this line here, my test wouldn't break if I called it.

[14:53] (external link, opens in new tab) Okay? Because it's nice by default. If I want to verify that in that something got called, I would say verify mock log, write, and then just pass it the string. Okay, so I verify I pass it the the fake object and I basically call this method with a string of some sort. Let's see the more complicated case.

[15:30] (external link, opens in new tab) So when logger throws it calls the web service. So here I'm creating a stub logger using the mock method. You see how confusing that is. And I and if I want to simulate something I will use the do throw method. And the reason I have to use do throw is because this is a void method. If this was returning something I would do it at the end of the API. But for now just learn about this. Do throw new out of memory error when stub logger.write.

[16:05] (external link, opens in new tab) Okay. For nonvoid methods I would just start with when and then I would say return then return blah blah blah or then throw. So do throw out of memory when write is called. And notice that here I'm saying that it could be any string. This is what's called a parameter matcher. Instead of saying a hard-coded string and then having to change it if the production code changes, I just say dude, it's any string.

[16:35] (external link, opens in new tab) What's nice about these matchers that I can also use them here. I can say, was this called with any string? Then I wouldn't care about the string. I can also call it with a string that contains something, which is a more maintainable way. If you do have to look for a string, it's better to look for something in the string. Otherwise, you might have new lines and tabs. Yes. >> Can you refer back to things that you that you set up? You said you're any

[17:05] (external link, opens in new tab) Can you can you quickly verify that the same string for example got out or got called. >> So I'll show you how to do verify. This is right now this is simulation. >> Okay. This is how I would verify. verify that the mock web service.write was called with something that contains out of memory error. Does that answer your question? >> Because I if I can say that

[17:36] (external link, opens in new tab) Remember this is a stub. I don't want to assert anything on it. >> Yeah, that's probably >> okay. I'm not testing what that output is. >> No, you don't. You do not usually for maintainability sake, you do not want to test what's in here. And the reason is because the more specific you are, the more you will have to change your test later. And the more specific you are, the more it means that you're setting up something very complicated. And tests should be simple.

[18:07] (external link, opens in new tab) Remember, you're just testing one small scenario. In that small scenario, it your stub logger might as well return any an error for anything unless you specifically know that some other objects might be calling it and then you have to take care of that and say look but if I get this then don't throw an exception. Yes,

[18:37] (external link, opens in new tab) >> the question about the annotations. Yes, you're using >> you're using the annotation the annotation style. Yeah, >> instead of the >> instead Yeah, you could use both. I for one think that naming the variable correctly and just using the simplest API that could possibly work makes the test more readable. The more annotations you have, the more you have to refer back to the top of the test. U but this is more of a stylistic choice. I don't really mind too much if you use annotations at the top of the test. I specifically don't like that style, but I don't have too many reasons not to like it.

[19:11] (external link, opens in new tab) It's more about personal choice. What I would recommend is whatever style you choose, you be consistent throughout the team. So that it's easy to read the test by that someone else wrote. So we have the arrange, we have the act, we have an assert. And that's one of the things to like about things like Mockito Okay, I want you to go back to the exercise and do the exercises and features until you see something that says wait for demo or anything like that.

[19:47] (external link, opens in new tab) Okay, and then just raise your hand and call me when that happens. But you have to do it in pairs (external link, opens in new tab), remember?