Episode 80: Unit Testing, Mocking, and AMOSS with Rob Baillie | Salesforce Developers Podcast

Rob Baillie is a Senior Technical Architect over at makepositive. Rob is passionate about extreme programming, something he has developed throughout his career. We discuss how that came to be in this episode.

Rob’s experience with extreme programming has always had a solid foundation in unit testing.  We go into how mocking works and specifically how his project, AMOSS, helps developers implement it.  Tune in to hear more about this from the very knowledgeable Rob Baillie.

Show Highlights:

  • How Rob got interested in computer science.
  • How he eventually got into the Salesforce ecosystem.
  • What his transition from PHP to Apex was like.
  • What mocking and test doubles in unit testing are.
  • What StubProvider does and why it’s so powerful.
  • What AMOSS is and how it makes unit testing easier.
  • The difference between test stubs and test spies.
  • What a brittle unit test is and how to avoid it.
  • Why we should view unit testing as documentation.

Links:

Episode Transcript:

Rob Baillie:
I was very lucky to get the job with a CIO who knew about these things and wanted to do this. So I went into the job interview there and basically I splurted out about extreme programming and this is the way I want to work. I think it’s going to be fantastic. And you kind of got halfway through the interview. And I’m thinking, well, have I just ruined my chances here? And luckily, he did just kind of turned around and said, “Well, yeah, that’s exactly what we want to hear because that is exactly what we want to do.”

Josh Birk:
That is Rob Bailie, a senior technical architect over at Make Positive. I’m Josh Birk, your host for the Salesforce Developer podcast. And here on the podcast, you’ll hear stories and insights from developers, for developers. There, rob is talking about his experience getting a job where he got to be passionate about extreme programming and other things like unit testing. And today, we’re going to sit down with Rob, we’re going to talk about unit testing. We’re going to talk about mocking. And we’re going to talk about Rob’s own framework, Amos, but where we start is how on apparently a very, very nerdy level, Rob and I are mortal enemies.

Rob Baillie:
I started coding when I was about six or seven, the story goes, on a Commodore Pet. So quite a long time ago. But then when I went through school, I decided I wanted to be an architect. And so I went off to university and studied architecture for three years, then came out the back of that and realized I’d have to do a lot more study. And I still wouldn’t get paid an awful lot. And friends at university were doing electronic engineering and computer science. And I just realized that that was something that I was already good at and that I could switch into. So yeah, I had the opportunity to stay on at university and do my master’s in computer science. And yeah, got into work pretty soon after that. And that’s more than 20 years ago now.

Josh Birk:
It’s okay. I think on another episode I mentioned one of my first computing experiences was on a TI 99. And I spent a good deal of my formative years in computing on a Commodore 64. So you’re in good company with vetting how long you’ve been [crosstalk 00:02:13].

Rob Baillie:
Yeah, [inaudible 00:02:13] guy. I don’t think we should talk to each other. I bet you had an Amiga as well, didn’t you?

Josh Birk:
I’m going to confess I had several Amigas.

Rob Baillie:
Oh, we’re mortal enemies then. I’m sorry.

Josh Birk:
Okay. Well, it looks like you have a lot of experience in Oracle and PHP. How did you get into the Salesforce ecosystem?

Rob Baillie:
Yeah, I was working at… I worked for a long time at one company and we built on Oracle under PHP, JavaScript. And they were looking to go into the cloud, the systems that we put together had been in place for 11, 12 years and we’re still holding up really well, but they had this idea that they wanted actually make a package up what they had already built and then see if they could sell it on.

Rob Baillie:
There wasn’t really a goer and Salesforce was a real good choice for them to do that. So I was running in the development team at that point and we moved over to Salesforce and cross-trained everybody at that point. So everybody in the development team had been working in PHP and Oracle for, I guess, like seven, eight, nine years each probably. We had some Siebel developers as well. So they transitioned at the same time. And as I came out the back of that, I managed that team for some two or three years after they moved into Salesforce. And I decided I wanted to move back into development myself. Management was something which worked for me, but it wasn’t a passionate sort of thing. So when I started looking for jobs, as I came up with that, rather than looking for Oracle and PHP, I looked at Salesforce because it just seemed to make a lot of business sense.

Rob Baillie:
And I admit there’s definitely as an aging developer, there are things about Salesforce I don’t enjoy. It’s not the nicest of development environments as a pure techie developer, but it just makes so much business sense that it’s really, really hard to ignore. And some of the stuff that you do get for free, it just means that a lot of the really dull stuff you just don’t ever have to worry about. So yeah, it’s a real kind of love hate thing with Salesforce I think.

Josh Birk:
I mean, I think that it’s interesting to say that because I definitely hear that a lot from Java developers who are kind of used to being able to build their own castle and then kind of live in it. What was the transition like between going from, for instance, from PHP to Apex? And maybe not just for you, but like for your team? Like eight years of PHP is a decent backbone in that language.

Rob Baillie:
Yeah, absolutely. And I think we were really helped by the way we approached PHP because PHP, it has a reputation as being a very scripted language. I mean, it’s an interpreted language. You don’t tend to compile it unless you’re doing things pretty serious. And it has a lot of similarities with JavaScript in terms of certainly back then. And the sorts of things you can do, it’s dynamically typed. It’s very weakly typed. So unless you want to, you don’t have to do an awful lot of the kinds of things that you have to do with Java. And so, it can be very succinct in its code, but the way we approach PHP was we always wanted to do things in a very rigorous way.

Rob Baillie:
And so right from day one, in that the company I was working for, we pair programmed and we used unit testing. And this was back in sort of 2003, 2004. So unit testing was up and running, but it wasn’t really that common. So I think that was in 2003. It was probably even a couple of years before that. Yeah. So when we approached PHP, we still approached it with the kind of rigor that you would have to use for a strongly typed language, even though it wasn’t really on Vogue at the time, it wasn’t really what everybody else was doing. And part of that was building sort of scripting engines for doing automated testing. So it was a rather time that Selenium had only just appeared. So we, at the time, we wrote a scripting language to drive IE6 through Com, so it would interpret our user interface components and we would be able to drive behavior through that. Then wrote a script.

Rob Baillie:
And now, which on top of that, that meant that we could write quite straightforward user interface tests, and it was slow. It was none of this headless browser stuff. It launched 1E6 and you could see it doing better then. It’s kind of magical, but also just incredibly slow. That sort of meant that when we then moved into Salesforce and Apex, and one of the first things that people got quite excited about was the idea that Salesforce were putting unit testing front and center. And it was one of the first things that you learned when you picked up Apex was unit testing. You have to have code coverage for your unit tests. And that’s where you start. It came as a bit of a shock then when you kind of get into the Salesforce world and you realize that even though that’s the case, I guess, because people are forced into it rather than they sort of grow into it, which is what we’d done. It’s not necessarily front and center of the individual developers’ minds in the same kind of way.

Josh Birk:
So let’s get into more specifics and I want to center a lot of stuff around your [inaudible 00:07:39] colleague talks with center around your product, Amos. But before we even get into that, let’s get into just some real level settings. So generically speaking, what does mocking mean in unit testing?

Rob Baillie:
Right. So yes, mocking, it’s a term that gets sort of banded around quite a lot and it sort of means an awful lot to a lot of different people in different contexts, but essentially what it is-

Rob Baillie:
… A lot of different people in different contexts. But essentially what it is about is when you’re testing a part of the system and you need to bring in dependencies, so you need to bring in other objects into your test, instead of bringing in the real world versions of those objects, you can create test only replicas of those so that you can divorce yourself from the dependencies on the other areas of the system. So a great example of that, and Salesforce does have mocking capabilities in it, so people will probably be aware of the term from the mock HTTP call-out, and it’s pretty clear what you’re doing then. When if you make an HTTP call out in our unit test, it’s not going to work, it kind of blanket fails. And so you have to bring in this mock HTTP call-out interface, implement an object, and essentially what that does is it replaces the HTTP communication for you, doesn’t it?

Josh Birk:
Mm-hmm (affirmative).

Rob Baillie:
Mocking in general takes that further. And so you can apply it to any object in your system. And so you can create an instance of an object that looks like a real world class, but doesn’t behave like that. It tends to be a very benign thing. So it doesn’t actually have any functionality of its own. All it will basically do is it will take an input and then it will respond then with normally a predetermined set of outputs.

Josh Birk:
Go you. And these replicas, that’s what you refer to us as test doubles?

Rob Baillie:
So yes, test doubles is a good term. People tend to use, as I said earlier, words like mock and stub and fake interchangeably. And for me, all of those things fall under this umbrella of a test double, because it makes it very clear. A double, it’s a replica of something and it’s four tests. Whereas if you tend to use stub, “I’m just going to stub this bit out. I’m not finished building it yet, so I’m just going to stop that thing.” So I’ll get something that reacts and it’s a temporary thing that you can then replace later. But a test double isn’t temporary, it’s something that’s going to live inside your tests. And then inside that you can have different behaviors.

Rob Baillie:
So a simple thing would be a test stub, which will just respond to method calls in particular ways. So you make a call against the method, it will respond with a particular value. You give it a particular parameter, responds with a different value. You can then enhance that and then have an entity called a spy where it behaves just like a stub, but after it’s finished doing what you’ve asked of it, you can then ask it what parameters were you passed? How many times was this method called? What parameters were used in that call? And then a mock is another layer on top of that, which is quite a clever piece of kit, which can then sort of self validate. You set it up to say, “These methods are going to be called in this order. And they’re going to be called with these parameters or parameters with these characteristics.” And then after your test is run, you can then say, “Well, did everything happen how you expected it to?”

Josh Birk:
Okay.

Rob Baillie:
And it’s a very different kind of way of thinking, because we tend to think about unit tests as being, “Well, I’m going to set up. I’m going to execute something. And then I’m going to assert that things have happened.” Whereas, mocks tend to turn that a little bit around. So you create a mock and you say, “I’m going to expect these things to happen.” And then you push the button.

Josh Birk:
Got you.

Rob Baillie:
And then it automatically checks for you that that has happened.

Josh Birk:
Got it. Okay. So natively in Apex, we have the HTTP mock. And I want to say… Is that the only thing that moves in this direction or are there other things that are standard in the Apex language that go around what you’re talking about right now?

Rob Baillie:
Yeah, well as a developer who did a lot of unit testing early on coming into Salesforce, one of the things I got really excited about when I first saw it was the idea of the StubProvider. So coming back just briefly to the PHP world, one of the great things about PHPs, which is similar to JavaScript in this sense, you can just build something on the fly and use it interchangeably. And the way it’s interpreted means that when I reference a class, it doesn’t have to be the real version of a class that’s in the system, you can just swap out files in the background and nobody’s any the wiser. I just import a different definition. And so you can create these replicas of things very easily by just doing a little bit of smoke and mirrors, if you like.

Rob Baillie:
And StubProvider sounds very much, and in fact it is very much, this same sort of implementation just embedded within the language. And it’s actually very, very powerful. So what the StubProvider allows you to do is basically you just define a class which has a single method on it that handles method calls. And then you can use that to instantiate an instance of any of the class. And I say any other class, it has quite a lot of limitations. But so yeah, let’s say we have a class DHL delivery provider and that’s got some functionality in it that communicates with DHL and says that I want to make a delivery using DHL. We don’t want to use that class in our tests. We want to use a replica of it.

Rob Baillie:
So I can create a class which implements a method, which is going to handle all of the method calls against that provider. And then that implements the StubProvider interface, and then I can just basically generate an object. And I can tell Salesforce it is this DHL delivery provider. And as far as Salesforce is concerned from that point on this object that you have in your test is a DHL delivery provider. And it will behave how you tell it to, but it isn’t the real world object, it’s one of these test doubles. And as I say, that’s baked into the language as a StubProvider and not a lot of languages have that. Everything else, it tends to be an extension or a library that you add on. So to have that embedded in your language is quite a powerful thing.

Josh Birk:
And I’m guessing you just reached for the DHL provider example specifically, because it sounds like the use case for this is you have a troublesome class that’s doing things like multiple HTTP call-outs or other things that are going to create a black hole for your unit tests. And here you can just kind of plug that black hole in.

Rob Baillie:
Yeah, absolutely. You’ve said that much more succinctly than I have. Absolutely.

Josh Birk:
All right, so then let’s move to your project. First of all, what does AMOSS stand for?

Rob Baillie:
So AMOSS is apex, mock, objects, spies and stubs.

Josh Birk:
Got you. So the library of the things that you were just describing.

Rob Baillie:
Yeah, exactly. And the reason for having this is although StubProvider is very powerful, it’s actually quite clunky to use. And so after that immediate excitement of coming into, “Wow, Salesforce has got unit testing front and center.” We’ve got this StubProvider, “Oh, it’s so hard to use.” It really is. Because what you tend to find when you use mocking frameworks in other languages, and I’ve got to be absolutely honest, AMOSS is not a brand new concept, this is something that exists in lots of other languages under lots of different names-

Josh Birk:
Okay.

Rob Baillie:
The thing about how you use a mocking framework normally in lots of other languages is when you want to create one of these test doubles, you do it inside your test and you basically say, “Well, I want a double of the DHL delivery provider, and when I call this method, it’s going to return this value. And when I call this method, it’s going to throw an exception. And when I call this method with this parameter, I’m going to-

Rob Baillie:
Throw an exception. When I call this method with this parameter, I’m going to return this different value.

Josh Birk:
Gotcha.

Rob Baillie:
And the great thing about that is it’s right front and center in your test. So as you’re reading through your test you say, “Right, well this is how this is going to behave, and this is how this thing’s going to behave. And I’m going to plug those things together and push the button, and yes, it does behave like that. Brilliant.” Whereas what you find with StubProvider is that definition of that behavior, by its very nature, it has to be defined inside a class, and that class cannot be defined inside your method. It has to be defined elsewhere.

Rob Baillie:
Now, it can be inside your test class. So you can use inner classes, but it’s not there. It’s not right there. And so you end up sort of jumping through hoops to get your test readable. And that’s very important. As anybody who’s ever visited a unit test for the first time when it’s failed. So you’ve never seen this thing before and you want to get this work finished, but why has this test failed? What is it trying to do? If you’re going to have to start jumping around between lots of different classes to find out what the test is even trying to do, it makes it quite difficult. And so that’s really the motivation behind Amos, is to bring that back in so that your setting up of your mock object our your testables is right there in your test. And so it’s very clear what’s happening.

Josh Birk:
Got it. So it’s providing structure, but possibly even more importantly, it’s providing readability so that when I look at your mock object, as you were saying, you were setting up the X, Y, and Z, and then pushing the button, as opposed to the other way around. I could easily see what that XYZ actually is.

Rob Baillie:
Yeah, completely. And that readability is a really vital aspect of unit testing, which is why I wanted to write this. I’ll be honest, I wrote this because I wanted to use it. It’s always a great starting point for anything, isn’t it?

Josh Birk:
Yeah.

Rob Baillie:
And the reason why I wanted to use that is because I want to write unit tests that can be read. Code lives for a long time and it’s going to be read a lot more times than you imagine it’s going to be read.

Josh Birk:
As any developer who has had to go back and read their own code will definitely attest to.

Rob Baillie:
Yeah. Completely.

Josh Birk:
So clarify a little bit more for me. You were talking about test stubs and you were talking about tests spies. And I’m not actually going to confess here. I think in prepping for this interview, this was the first time I’d really seen the concept of a test spy. So compare and contrast those two concepts for me. What does one do versus the other, and what’s their pros and cons and their strengths and weakness?

Rob Baillie:
Yeah. Okay. So a stub is a fairly straightforward entity. So it’s a testable that has the least amount of functionality you can imagine. It really does just have an interface that you can call. So it has methods that you can call and they’re going to respond with particular values. And in general, what you use a stub for is purely for directing a test down a particular path.

Josh Birk:
Okay.

Rob Baillie:
So it might be that you want to get some data from, from the database, and so you use it a data source or a selector in the financial force library terminology. And your stub is a replica of your data source, only it just returns particular records for you. They don’t have to be in the database. I want to get a list of six products, it just returns you back six products.

Josh Birk:
Got it.

Rob Baillie:
And that’s that. A spy will do that, so it’s got all the capabilities of the stub, it’s just that once you’ve finished using the spy, you can then ask it, “Well, what were the values that were passed into you? Was this method called? Yes or no? And if it was called, what were the parameter values that were passed through it?” And so what you tend to use those for is not just directing your tests down a particular path, but proving that that object is used in the right way.

Josh Birk:
Gotcha. Gotcha. So it sounds like they’re not purely distinct concepts, but almost more like layers of functionality to be able to ask more complicated questions to them.

Rob Baillie:
Yeah, completely. And the reason why I tend to talk about the difference between them … And if you look at Amos, there actually isn’t a distinction between stubs and spies in their implementation. They’re implemented exactly the same way.

Josh Birk:
Gotcha.

Rob Baillie:
Basically, a stub is just a spy that you haven’t asked the questions of. But the reason why I like to talk about them as different concepts is because it helps to drive the thinking around why you’re using particular bits of functionality. I think especially when people first start using mocking frameworks, they tend to kind of go overboard and say, “I can use this everywhere. I can stub everything. I can lock everything.” And then I’ve got to ask every … Was every parameter passed in exactly the right way?

Rob Baillie:
And most of the time you just don’t need it. Most of the time, you don’t need mocking at all, if I’m absolutely honest. It’s a very particular tool for a very particular job. But when you do use it, it’s very important for you to understand why you’re using particular bits of functionality. And these terms help to solidify that in your mind, I think. So it’s a real [inaudible 00:21:34]. So as I say, stubs are very good for driving direction in tests, then spies are very good for checking that that object is used in the right way.

Josh Birk:
Got it, got it. So let’s talk a little bit about good unit testing, bad unit testing, a little bit of philosophy of unit testing, but start with the bad. How do you define a brittle unit test, and in what are some things that people can think about to avoid writing them?

Rob Baillie:
Okay. So a brittle unit test. That’s a particular type of bad test, because there are a lot of different types of bad tests.

Josh Birk:
Right.

Rob Baillie:
Define what you mean by brittle, first of all, I guess. Which is … So I’m guessing you’re talking about a unit test where when you start to change an area of the system, lots of tests start to fail.

Josh Birk:
Yeah, yep.

Rob Baillie:
And so there are lots of different ways in which tests can be brittle. I think the main one is this kind of brittleness based on dependency, which is the one that we tend to see most, I think in Salesforce if you don’t use something like a mocking framework. So I’m testing class A, and in order to test class A, I need to bring in class B. And the unit test for class A relies on the behavior of B in order to work. I can’t check the behavior of A directly. I’ve got to interpret it by what B does, and what you then find is class B changes cause class A’s tests to fail. And it’s kind of hard to really explain that without having a piece of code in front of you, but it’s really … That dependency brittleness comes about when everything’s so intertwined and interlinked that it’s very hard for you to test an individual bit without needing an awful lot of other stuff.

Josh Birk:
Untying the knot, so to speak.

Rob Baillie:
Yeah, so I think one of the things we tend to think about Salesforce development as being this … It’s a data processing thing, isn’t it? We tend to get data and then we process it and then we put it somewhere.

Josh Birk:
Right.

Rob Baillie:
And we tend to think of that as a fairly procedural thing. And when you make that kind of procedural code, what it means is I can’t test that I’m doing the right things with stuff, unless I’ve created a lot of data at the start. I have to do a lot of setup, and then I-

Rob Baillie:
I have to do a lot of setup and then I run my things, and then I’ve got to test that a lot of things happen.

Josh Birk:
I think that’s solid. It also brings back a loop that one way around that is a marketing framework which provides you with a flexible yet reliable source of those presumptions.

Rob Baillie:
Yes, and it also forces you to start thinking about how you draw those lines between bits, and in the classic of obviously trying to develop that way of thinking about it, we think about those classes should have one responsibility. They should only do one thing and they should do that well, and they should hide their implementation from the outside world. These kinds of concepts are quite easy to talk about, but they’re often quite hard for you to build, especially when you’re stuck in this way of thinking about how I’m going to get data out, I’m going to process it and then I’m going to insert it.

Rob Baillie:
It’s very hard to see where the line’s drawn between things. One of the things that we found when we were using mocking frameworks back into PHP world was that the looking for areas where you could split that dependency so that you could more easily test them was actually a good way of working out where the boundaries are in your functionality and being able to abstract things in a way that makes sense. Because in a way, when you’re writing these mock objects and you’re writing those unit tests and you’re doing it at the same time as writing your code, so doing it in a test driven development sort of way, what you’re kind of doing is writing the API for all of your bits as you’re building the tests for them, and you’re building the bits all at the same time.

Josh Birk:
All in the same process. Well, let’s take that one step further because you talk about you had to test in a very specific way. Test driven developments, one way to think about it, but it feels like you’re thinking about it like that plus plus. Your other talk that you’ve given it as unit testing is documentation, so define that goal for me.

Rob Baillie:
Unit testing is documentation. Yeah, completely. That’s something that I feel quite strongly about, and as I’m sure I’ve said before, unit tests and systems live for a long time, and the first time somebody sees a unit test is normally when it’s failed. And really, when somebody’s going into a unit test in that situation, what they’re trying to do is they’re trying to find out, well, what was this class or what was this method supposed to do that it now doesn’t do? And in that sense, the unit test is a documentation for all of the intentions that all of the previous developers have ever had for all of the methods and all of classes that these unit tests exist for. It’s a list of, these were intentional decisions in terms of how this should behave.

Rob Baillie:
And we really want our developers, when they look at those, to be able to read them and say, “Yes, I see what this developer intended this to do.” And not just that, but I can see how the developer intended this to be used. And often, when we’re writing tests, we forget about that aspect. What we tend to think of unit tests is being four, and they absolutely are for this, they’re for proving that functionality exists in a particular shape. And we shouldn’t dismiss that at all. Absolutely, that’s the 100% the most important thing that unit tests are for. But if we also blend bringing into the idea that, well, people are going to have to read them and they’re going to have to understand them, and they’re a very particular form of code. What I think we really owe our future selves, we owe it to them to write our tests in a way that means that if I start at the top, I can see a little snapshot of what this method does.

Rob Baillie:
So we would normally start with a test that goes through the happy path and say, well, this is your basic, I’m going to set this up and if you do this, everything’s right, I should get this result. And then we can layer on top of that some other ideas, or actually, if I set this value to a slightly odd value, then I’m going to get this slightly odd behavior and it’s going to work like this. Or if I, instead of giving it one record, I give it 10 records, it’s still going to work, but it’s going to do this instead. We can sort of layer that story into our tests piece by piece, rather than trying to blast them with all the information in one go. So take the future developer on a little journey on how this class is supposed to behave.

Rob Baillie:
But then on top of that, we can help them by shaping things in particular ways so that we describe our intention within our tests, we’re expressive in our tests about what the behavior is. So we use very expressive variable names. We use… That string that’s not important, actually make it say unimportant. So that things can blend into the background when they’re not important and can come to the front when they are. We shouldn’t be scared of using functions to hide a set up that’s not important, but then we shouldn’t use functions to hide set up that is important. It’s those kinds of little decisions which are quite different to the kinds of decisions that we make when we’re writing the code in the rest of the system.

Josh Birk:
And that’s our show. Now we will be linking to Rob’s material in the show notes if you would like to dig into that deeper. Now, before we go, I did ask after Robs’ favorite non-technical hobby, and well, things got complicated.

Rob Baillie:
Ooh. Now, see, I’ve been thinking about this one. I knew this question was going to come up because I have listened to previous podcasts. And yeah, I mean, I want to say it’s either running or snowboarding.

Josh Birk:
Okay.

Rob Baillie:
And snowboarding is a hard hobby to have in the UK during a pandemic. It’s just, I’ve not been able to do anything. But as I was thinking about it, I think actually, my biggest hobby is acquiring hobbies.

Josh Birk:
Okay, so-

Rob Baillie:
What I tend to do is I find something that’s going to get me interested and get me excited and it does for about six months or nine months, and then I feel like I’ve kind of got a handle on it and then I need to look for another hobby. Which means I’ve got loads of hobbies I’m really bad at.

Josh Birk:
What’s the most recent one?

Rob Baillie:
Oh, writing open source software, but that’s technical, isn’t it? No, I have got into brewing beer actually. That’s a really good one. That’s got a good payback.

Josh Birk:
You know, I am shocked that you were the first person, I think my memory is correct on this year. The first person that you answered without any context of non-technical hobbies, because there was this weird moment in developer community evangelism where I swear, every table I went to, there was one person talking about their home brewery. And then I think everybody just got bored with it or something, so yeah.

Rob Baillie:
Yeah, I’m late to the party.

Josh Birk:
I want to thank Rob for the great conversation and information and as always, I want to thank you for listening. Now, if you want to learn more about this show, head on over to developer.salesforce.com/podcast, where you can hear old episodes, see the show notes and then links to your favorite podcast service. Thanks again, everybody and I’ll talk to you next week.