Don't Mock 3rd Party Code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in test driven development mocking is a widely used and somewhat widely derided technique people argue about whether or not it's a good idea in the first place and complain that mocking is complicated and represents a tight coupling where we don't want it between the test and the code being tested I think the mocking is an extremely valuable tool in test driven development but I've also seen it badly misused one of the more common ways that it's misused is to use mocking tools to generate mocks for third-party code I think that this is risky and is usually a pretty big mistake so today let's explore mocking and why it is a bad idea to Mark third-party code [Music] hi I'm Dave Farley of continuous delivery welcome to my channel if you haven't been here before please do hit subscribe if you enjoy the content today hit like as well let's start by thinking of the three kinds of outcome that we may be interested in from a unit test first we may be interested in a simple return value of some kind we calculate something and expect that return value so that in our test we can set up a calculation and insert the value that we get is the one that we expected next there may be some change in state of some kind so that we set up a change in of State in our test and assert that the state has in fact changed in the way that we expected it to but then there's another kind of outcome that we may perfectly reasonably be interested in when we interact with code that we are testing it in turn may need to interact with something else and we'd like to confirm to assert that that interaction actually happened it's also the most complicated of the three types of unit tests this last one is the province of mocking and our Focus for this episode it's also the most complicated of the three types of unit test and the easiest to get wrong before I go any further let me say thank you to our sponsors for today's video we're fortunate to be sponsored by equal experts trisentis transfig and Roost all these companies offer products and services that are well aligned with the topics we discuss on this channel every week so if you're looking for excellence in continuous delivery and software engineering click on the links in the description below and check them out let's think for a moment about what a unit test is and what it's really for the simple response is that a unit test is a test and of course that's true but I think it's also more usefully thought of as a measurement of our system if we're writing a piece of code how can we tell if it does what we expect it to we could write the whole thing the whole system run it and try stuff out by hand this is a bit ad hoc a little bit random but it is certainly better than not trying out our code at all I suppose one of the problems though with this approach is that it isn't very organized it's very easy in fact completely normal for this approach to leave big gaps in our testing and in our understanding of our code as a result it increases the likelihood that we'll miss something important and that leaves in turn the result that we might end up with bugs in the system imagine two Carpenters making a chair Carpenter one doesn't measure anything they just look at the wood guess how big and what shape each piece should be and then they start cutting it and attempt to assemble all of the pieces into a chair if they are a good Carpenter it's conceivable that they may end up with a working chair this way but it won't be a very good chair however good the carpenter there will be gaps between all of the pieces and it'll probably be a bit wobbly when we try to sit on it it will probably break pretty quickly too when we use it the second Carpenter measures things even if they aren't working to a precise plan as they build the chair they measure things as they go making sure that things like all the legs are the same length and so that the various pieces all fit together more precisely their chair will almost certainly be stronger and more comfortable as a result measurement allows us to make things that fit together better and work better I think of unit tests actually all automated tests as a form of measurement really does my code do what I think it does if this is about measurement then we need to be careful how we measure things we'd like our measurements to be controlled and repeatable when I learned the basics of carpentry at school we were taught to choose references that we'd always use to measure things from we'd pick the flattest side and the best surface for the piece of wood that we were working with and we'd mark them and then always measure from those sides this made our measurements more repeatable and more reliable if our tests are measurements of our code then we want to try and achieve the same kind of thing in code this may be less obvious but I think it's the same idea really to put this into more technical language we're trying to get our system into a precisely correct State for the start of a test so that then we can interact with it and measure its responses in a predictable way as I said earlier there are only three types of response that we're ever really interested in and the interaction with other parts of the code is a perfectly valid kind of response for some parts of the system so we need to measure those kinds of response too but we also need to be careful when we measure them we know that we're not compromising too much and that we're measuring in a way that is sensible in the context of the software that we're building we want measurements that helps us to design better Solutions not that couples us too firmly to a particular implementation that we can't move without breaking the measurement so back to our mocking a mock is a specific kind of test double that is something that we use in the scope of a test to help us to test the part of the system or measure it that we're interested in test doubles can actively help us to measure our code or they can just stop us having to create more complex things in the scope of our test sometimes mocking describes a specific kind of test double there are others a mark is used to verify the output from the code that we are interested in confusingly mocking libraries often do quite a lot more than only that because they're often also used to create other kinds of test doubles which are not strictly mocks at all so that the terminology isn't really helping as much I recommend this website if you really want to dig into the various definitions of test doubles in more detail fundamentally though I think that we can usefully think of all of these things as forming part of the measurement of our system if we want to supply a specific return value for a call to another part of the system or if we want to record that we called another part of the system during the course of some operation that we're interested in testing and later assert that that call did happen then we are really just trying to measure our code in controlled circumstances so that we can get those repeatable results that I mentioned earlier now the other significant part of test driven development is that our goal is to use tdd as a way to help us to improve our design we can do that by preferring code that's easy to test and code that's easy to change if you're used to writing unit tests after you've finished the code rather than before then this may sound surprising because what happens when you write the test after the code is that the code and the test end up being tightly coupled together so now our code is hard to change without either breaking or Worse invalidating our tests if you'd like to learn more about how to do this in a lot more detail than I can sensibly cover here please do check out my test driven development and behavior driven development designed through testing training course which explores all of these ideas and more there's a link in the description below so our goal is a bit more complex than simply wanting to test our code we want it to be relatively easy to change our code when we need to as well and ideally to keep our tests running so that they can act as a defense against us breaking things fundamentally this means that we need our tests to assert things that matter but not care about things that are only implementation detail this gets us to the hard part really of test driven development which actually has very little to do with test driven development and is really all about design tdd isn't difficult but good design is tdd merely helps us to surface the problems with our design so if as part of our design we're interested in interactions with other parts of the code base what matters what doesn't and what counts as good design rather than bad for me it's this is nearly all about the ideas of managing complexity in code even at the most fine-grained level the point at which your code jumps between contacts whether those contexts are functions classes modules services or systems these are key points in the design always our aim should be to hide detail at these points that's really what all of these code constructs are there for in the first place so in a good design each part of our code keeps some secrets from everywhere every other part the job of even a function is to abstract the code just a little bit so that I can then use it without caring too much about the detail of actually how it works so when we're making design decisions about how to break up our code into separately testable separately measurable small pieces what makes our life easier and what makes it harder our life is easiest when the conversation between the different parts of the system is minimal this is pretty obvious really because then there's less work for our code to do we want to avoid overly chatty exchanges between different pieces of code this is the driving force between design guidelines that we often hear like tell don't ask it's better to tell another piece of code to do something rather than ask it for all of its data and do it yourself let's imagine for a moment that I want to store a book the book details are entered into a web page and my aim is to store the results in a database I could write code like this I wouldn't but I could I think that this code is pretty awful to be honest but we'll get to that I retrieved the book title author and ISBN for the book and store them as a record directly into my database this will work but it's a really terrible separation of concerns this matters because this code is extremely fragile a small change will break it all and now fixing it will mean me holding all of the details in my head while I do so any number of changes to dependencies or logic will require me to change this code on the whole good code as it has a single reason to change this is not good code this is not modular and cohesive either and there's no abstraction and there's pretty nasty coupling going on here let's look at the test for this code I'll confess this took me quite a while to write because I needed to deal with the niceties of some more complex mocking in Python now I don't usually use these sorts of features in mocking libraries because I don't need to if I design code the way that I generally do then all I need is a very tiny subset of the features of my mocking Library most of the time I'll show you what I mean later the result of this is pretty Fragile with my test very tightly coupled to my code my test depends on the internal workings of in this case the app2 module this is very nasty the python Muk dot patch thing may be clever and Powerful but it's also highly coupled to implementation detail I can break this test by renaming variables this is terrible test I could certainly improve this by working to improve the modularity and cohesion and by increasing the separation of concerns in my design I could move the code that stores the book into a separate module for example here is the next step and I've certainly improved the abstraction here considerably and the modularity separation of concerns and cohesion too as a result I've also made this code easier to test in smaller pieces the code in my abstraction is very very simple but it also represents my current understanding of the problem accurately this is not upfront design this is just the problem as I see it right now when I learn more I'll change it but for now it works and it allows me to write more cohesive code I could still write an overmocked unit test for the plumbing at the edges of my system if I really wanted to like this but most of the time I wouldn't bother to be honest this code doesn't do very much now all that I really care about for this code is that the pieces are assembled correctly so I think that this is better done as part of an automated acceptance test rather than as a unit test in the acceptance test I'll deploy the application as it will be deployed into production and confirm that it all works together I can check that I'm getting real posts from the website and that the DB has really been initialized properly and then updated with the new book and so on if we mock the third party code here to make our code more testable we need to mock the web page and the storage in detail in this case we'd need to mock flask and SQL like 3 python libraries we'd need to simulate the generality of the interactions with those libraries because the third party code here is designed to be generally useful and it is so now our test is back to being tightly coupled to the solution in an acceptance test I can ask can I add a book and I don't care about the implementation details if I abstract my design and introduce a layer of code that does the interesting stuff though dealing with books then this code is clearer it does one thing and so it's easier to read and easier to change this code doesn't need to change when the page design changes for example or when the DB layout changes it's also a lot easier to test I actually wrote all of these tests in the reverse order to to how I've shown them to you I started with the last version the nicest code as far as I I'm concerned that I showed you here because that is how I generally write code so it was a lot easier for me to think about the problem that way it took me a few minutes I don't know maybe five or ten I then realized that I wanted to show you the alternative the messy alternative to be frank this took me ages several hours because the mucking in the test was so complicated that is not the fault of the mocking or that the testing is difficult it's because my code was rubbish there are lots of things wrong with this code but if we're practicing test driven development then it starts with the testing I'm not at all convinced that you get code that looks like this from practicing test first test driven development why on Earth would I want to make my life so difficult at the point when I'm writing the test and I don't have any code yet I don't think that I would ever willingly write a test like this by choice the problem here starts with attempting to Mark the third-party libraries the third party code is always going to be more generic less targeted to your particular problem than your code will be we're automatically to some extent leaving our design in our testing in the hands of the people who wrote the third party code if we start from this point this isn't because the third party code is bad but because it's designed to be generally useful and it is this means that it is inevitable that your interaction with the third party code will be more complicated than you need it to be If instead of mocking the third-party libraries to save you some typing you instead write a small often very very simple abstraction to represent what you really want your code is much simpler easier to read easier to test and ultimately better quality because it's now easier to change your tests are simpler too easier to write faster to execute and much less tightly coupled to the code that they are testing so they're less likely to need to change when you want to change your code or when the interface is to the third party libraries change tests like these are also a much clearer statement of what your code needs to achieve rather than an over complex tightly coupled record of how your current implementation works mocking third-party libraries is nearly always a bad place to start much better to create your own simple facade or wrapper to abstract your use of the third party code I think that the strongest argument for this point of view is when you're dealing with code at the edges of your system the places where IO occurs as in my example but this is also more broadly true I generally prefer to have a layer of my own code between almost any third-party code and the rest of my system a tiny usually very simple layer of translation between the abstraction of what it is that I'm trying to do in my system and the tech that's helping me to do it whatever that is this is a guideline though rather than a hard and fast rule I'm not always going to abstract all external code I usually at least draw the line at the basics of my programming language libraries for example so I'm not going to usually hide the use of things like strings or collections but sometimes I may even simplify the use of more complex language constructs if it makes my code easier to understand my goal is to always try to make my code easy to read easy to test and easy to change and mocking third-party libraries never helps me to do any of these things thank you very much for watching and if you enjoy my stuff please do consider supporting our work on this channel by joining our patreon community thank you very much foreign [Music]
Info
Channel: Continuous Delivery
Views: 38,804
Rating: undefined out of 5
Keywords: software mock, mocking software, mocking 3rd party code, 3rd party code, third party code, don't mock code, stop mocking code, software testing, test 3rd party code, software mock test, unit test mock, unit test, mocking software testing, programming, computer science, software engineering, software development, Dave Farley, continuous delivery
Id: v6hP2MXoVrI
Channel Id: undefined
Length: 19min 55sec (1195 seconds)
Published: Wed Mar 29 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.