Lesson 7 of 15

Using Bogus

Duration: 15:25 Watch on YouTube (external link, opens in new tab)

Markdown version

Learn how to use Isolation (Mocking) frameworks (external link, opens in new tab) and the difference between RSpec-Mocks, rr, Mocha and Bogus, and why Roy thinks Bogus is better.

In this lesson

Roy Osherove introduces isolation frameworks (external link, opens in new tab), which he prefers not to call mocking frameworks because they are mainly used to create stubs (external link, opens in new tab), and rewrites the first test with RSpec mocks, creating the fake (external link, opens in new tab) logger with double and asserting with expect to have received. He explains that RSpec mocks and Mocha are strict (external link, opens in new tab), so a test has to be told about every method that will be called and breaks for the wrong reason, which he compares to throwing away a pizza because his six-year-old didn't say thank you. He recommends Bogus, which is non-strict (external link, opens in new tab) and has safe faking that fails when a faked method doesn't exist or gets the wrong number of arguments.

Transcript

2,255 words

[00:04] (external link, opens in new tab) Okay, so now that we've worked with the handwritten mock (external link, opens in new tab) objects and stubs (external link, opens in new tab), I want us to talk about isolation frameworks (external link, opens in new tab) because when we talk about handwritten mocks and stubs (external link, opens in new tab), you can get into a lot of problems relating to maintainability (external link, opens in new tab). For example, if you have a method with many arguments, how do you how do you record that each one got called etc. you're going to get into a lot of boilerplate code. And this is where isolation frameworks come into play. Now, everyone else calls them mocking frameworks, but that's not a good name because they're not only used to create mock objects (external link, opens in new tab).

[00:39] (external link, opens in new tab) They're mainly used to create stubs. And I want to avoid that confusion. So, I call them isolation frameworks because they're used to isolate a unit of work (external link, opens in new tab) from the dependencies. So, we use isolation frameworks. The most notable one is built into RSpec. It's called RSpec mocks. And this is the basically the same first test written with RSpec mocks instead of with a handwritten mock object. So what we can see here is describe adding given the logger attached.

[01:11] (external link, opens in new tab) To create the logger we simply call the word double and it's based on the idea of a stunt double. called the test double but for in our mind you just call it a fake (external link, opens in new tab) object and then we tell logger to be able to receive a method called write now this is important without this I'm actually going to have a problem I won't be able to assert the method was called because RSpec mocks are what you would call strict (external link, opens in new tab) mock objects so mock objects

[01:48] (external link, opens in new tab) Or isolation frameworks in have behaviors and there are strict frameworks and or strict map object and non strict and the idea of strict is that with strict you have to tell it everything that's going to happen to it and if you don't it's going to throw an exception okay it will throw a hit at you that says dude no someone called write on on my object but you didn't tell me that was going to happen so I'm going to fail your

[02:22] (external link, opens in new tab) It used to be that this was a good idea until we found out it's not a good idea. But let me just finish the test and then we'll talk about why it's not a good idea. So we create a logger. We tell it what methods it's able to have to to fake and to receive and then we call string calculator (external link, opens in new tab) with the fake logger. Now to assert we use the word expect logger to have received a call on write with a string

[02:55] (external link, opens in new tab) And we can also have matches. So we can say with a with a string that matches something etc. So we have the arrange that's part of the arrange. Now this is the act and we have the assert. Okay. So what's wrong with this? On the face of it, it looks pretty nice. If I run it, it should say, oh, you I changed the signature. So now it says you need two arguments. So we're not going to worry about running it for now.

[03:27] (external link, opens in new tab) But u because I changed it to have a web service as well. But if I wanted to, I could just say web service equals double See what happens now. Okay, one example, zero failures. Okay. If I send in one,

[04:01] (external link, opens in new tab) I'm getting this expected got zero, but I actually got got one. So, I can easily assert the messages that my object got. That's very, very nice of them. I like that. But there's a problem here. If I want to assert that the method got called, I also have to set up that method to be called later. So there's first of all that creates duplication in my test. Second, this is a huge maintainability burden because I'm effectively

[04:34] (external link, opens in new tab) Telling the test everything that's going to happen internally to my object all the methods that might get called even if they have nothing to do with the current scenario that I'm testing. And this is very important to understand. If tomorrow the method write a different method on the object gets called and it has nothing to do with the fact that this code would still work, this test will fail because I have to go back to this test and add a line that says no that method will get called internally as well.

[05:08] (external link, opens in new tab) It's a huge maintenability burden. So the the analogy that I use is that let's say I I I send my kid out. He's six years old. I send him to to buy me a pizza. So I give him some money and I tell him, "Look, you go to the pizza place down the street and you tell the salesperson, "Hello, my daddy needs a pizza. Please bring me pizza that looks like XYZ." And then you say, "Thank you." You hand him the money.

[05:39] (external link, opens in new tab) He gives you a pizza and you come back immediately. And then my kid comes back maybe a few hours later. He got to the wrong shop or he got to the shop but then he missed the bus. Nobody knows what happened. He comes back you know all bruised and the you know dirty from the from the cold and he comes back with the pizza in his hand. He says, "Daddy, I got you the pizza." And then I take the pizza and then I call the salesperson in the pizza shop and I say, "Hey, did my six-year-old kid come by?" Okay.

[06:09] (external link, opens in new tab) What exact words did he tell you? Oh, he didn't say thank you at the end. Okay. Then I take the pizza and I throw it on the ground. That's basically the analogy. That's exactly what happens. Okay. All this nice effort goes to waste for something that is completely meaningless. So what in effect happens here? We are coupling the test to internal implementation code. What I would like to do is to have a non-strict (external link, opens in new tab) version of this that says look I don't care what methods you can call

[06:45] (external link, opens in new tab) At the end I can check whether one of them was called but don't break just because someone called a different method as long as the right method got called with the right argument at some point okay I call it the the hippie mock like dude everything is okay don't worry yeah that method was called as well yeah hund other methods were called but I'm cool with that but these mock objects are strict by default and there is no way to make them non- strict which is one of the reasons that I would not recommend using RSpec mocks.

[07:20] (external link, opens in new tab) Let's look at the same text test with a framework called Mocha. Mocha is also a very well-known u isolation framework in in Ruby. Mocha has a nice API. You create a double just like you did. Then you say ex logger expects write with hello at least once. For example, you still have to say before the execution of before the act part what's going to happen.

[07:50] (external link, opens in new tab) It has the same problem of being too strict. Okay. So the two most used frameworks in Ruby RSpec mocks and Mocha are are strict and I would not recommend using them until they would have a way to disable the strict behavior. Now I come from the .NET world and the Java world where this has already been figured out and many of the new frameworks already have non-strict behavior by default. So the tests are much more maintainable over time.

[08:24] (external link, opens in new tab) So there is a framework and I looked at it. If I look at ruby toolbox.org, the the fourth or fifth framework is called Bogus and that's the first one that actually allows this kind of non-strict behavior. It's actually really nice and that's the reason that I would recommend using Bogus in your tests. Let's see.

[08:57] (external link, opens in new tab) Okay. So this is the test has the two dependencies. This is the logger and this is the web service. So if I want to create a fake of the logger, I just use fake just like I use double and I give the name of the type with the standard naming convention. So slow slow logger is actually determined to be slow logger with the camel casing. So I create a fake of the logger. I create a fake of the web service and I don't don't do anything for the act part.

[09:28] (external link, opens in new tab) Then after the act part act part in the assert I just say logger should have received write with whatever text is. And this is exactly what happens is that it doesn't care that I I didn't tell it that right will get called. It just tells me if it did. So it's a very non-strict framework and it has some other extra benefits. For example, if you tell logger to for example, here's how I tell logger to throw an exception.

[09:59] (external link, opens in new tab) Okay, log or fake. And also for method write I want you to have this behavior proc raise no. Okay. So if I want to do that that's very easy but it's also has a feature called safe mocking which I just call safe faking of course. And the feature will actually throw an exception if you try to fake a method that doesn't exist. So if tomorrow because this is Ruby and everything is like dynamic.

[10:33] (external link, opens in new tab) If tomorrow you change the name of the method right to a different method and you're using a different framework, this test will continue passing happily. Okay. But with when you use framework like Bogus, this test will actually fail and say no right method found. So it will force you to to to fix the test because the test is wrong. Okay. So it keeps you safe from changing the APIs. If you send in the wrong number of arguments, it will also tell you that you're doing it wrong.

[11:05] (external link, opens in new tab) And that's important because it verifies for you things about the signature of the APIs that might have changed. So it's almost like in C if I removed an argument from the signature, I would have a compilation issue. So that's as close as you can get to to that kind of stuff. So this is me faking the web service and it should be this easy and this simple. Should have received notify with and then you can give it a lambda s includes logger blah blah blah through up

[11:40] (external link, opens in new tab) Questions. So it's much more preferable to use frameworks that support nonstrict isolation than frameworks that don't support that. And it's important to distinguish between frameworks that do frameworks that don't. And if there are no other question, let's see if I have any things that I forgot to to add about the exercise. the terminology we talked about that

[12:12] (external link, opens in new tab) We talked about the problems when we have manual fake of lots of repetitive logic and then we have the isolation frameworks so we have RSpec mocks mocha RR and then we have on the right side the new generation of isolation framework and Bogus is one of those and I think there are a couple more but I still have to to dig bit deeper but I would highly recommend to stay away the old generation. Although they're considered cool and simple and new, there are certain things that most people who do testing in Ruby don't realize that they're putting all this maintainability effort that they will have to do later into their tests that they shouldn't have to do and they don't know that they don't know this.

[13:01] (external link, opens in new tab) Okay, so this is an important thing to know. Okay, let's continue with the from the now you have to to exercise number two which is basically doing exercise number one using bogus and then you can move to exercise number three. Okay. >> Yes. I'm not sure I understand why.

[13:40] (external link, opens in new tab) >> Oh, okay. It would not. Okay, let's talk about this. If I go to this, you're asking how is this bad? >> Yeah, >> it's bad because if tomorrow somewhere inside string calculator, someone else has added another feature where they're calling logger write extended that they write even more information on top of the feature. It's like a separate feature. Your feature still works. They're writing a new feature calling a new API.

[14:10] (external link, opens in new tab) This test will actually fail because you have to say allow logger to receive write extended as well. >> So it's extra maintainability. That's why it's bad. It's because when someone else adds a feature that calls another API >> that you didn't specifically say in your test, the test will break for the wrong reason. the right method calls the right send then this will fail. >> No, if you change the right method, it won't fail because the right method is fake.

[14:44] (external link, opens in new tab) So, it won't actually run the real code. >> Okay. But if let's say that someone calls another API just in case like to check that something is true or to check the location or something, you have to add each and every API call that happens inside string calculator. in all the tests that have a logger, it's fake. Okay. And that's and that's that creates like a the effect is huge in terms of

[15:16] (external link, opens in new tab) Okay, other questions. let's go.