Episode 23 on Mocking was interesting. My question is: aren't mock objects (spys especially) piercing the encapsulation barrier. Is it wise to use Mock Objects when the implementation of a method/class is volatile? I recall in the previous episode on Clean Tests saying that the principles of Object Oriented Design apply as much to tests as to production code. Aren't Spies violating the principle of encapsulation?
My question is: aren't mock objects (spys especially) piercing the encapsulation barrier. Is it wise to use Mock Objects when the implementation of a method/class is volatile? I recall in the previous episode on Clean Tests saying that the principles of Object Oriented Design apply as much to tests as to production code. Aren't Spies violating the principle of encapsulation?
Split the original distinction of state-based versus interaction-based testing into two: state versus behavior verification and classic versus mockist TDD...
THERE IS NO SUCH THING AS MOCKIST TDD!There are different ways of designing how a system is organised intomodules and the interfaces between those modules. In one design, youmight split the system into modules that communicate by leaving datain shared data structures. In another, you might split the systeminto modules that communicate by synchronous message-passing. Inanother, modules might communicate by lazily calculated streams, or byasync messaging between concurrent actors, or by content-based pub/subevents, or...Depending on how you design inter-module interfaces, you'll needdifferent tools to unit-test modules (whether TDD or not).Mock Objects are designed for test-driving code that is modularisedinto objects that communicate by "tell, don't ask" style messagepassing. In that style, there is little visible state to assert aboutbecause state is an implementation detail -- housekeeping used tocoordinate the message-passing protocols.In other styles of code, procedural or functional, assertions onqueried state works fine. (In asynchronous code, they do not becauseof race conditions).Even in a system that uses Mock Objects appropriately in itsunit-tests, a significant part is purely functional in my experience,and so will not be tested with mock objects.Talking about Mockist TDD vs Statist TDD leads people to think interms of adopting a "TDD style" and rigidly following a process,rather than understanding how to select from a wide array of toolsthose that are appropriate for the job at hand.Rant over (this time!).
The difference between my approach and the GOOS book is a common issue. First, there _is_ a pretty big difference. They start at the periphery of the system and work in. I start in the center of the system and work out. Oddly, that's about where the difference ends. The "hexagonal architecture" that they promote is remarkably similar to the "Clean Architecture" that I promote.Why do these two techniques result in the same architecture? Because our goals and emphases are the same, and we're both firing tracer bullets. Both techniques are agile, both techniques recommend developing in tiny iterations, and both techniques push you end to end within the confines of an iteration. Both wind up with acceptance tests running through the GUI at the end of the iteration. And both want those acceptance tests written at the start of the iteration.Somehow people have gotten the idea that I don't think you should have tests at the GUI. This is probably because I often stridently assert that you should not test business rules through the GUI, and people infer that to mean _no_ tests of the GUI. OF COURSE you will test the GUI. I just don't want you testing business rules through the GUI. When write tests at the GUI, I want them to be GUI tests. And yes, this means that the technique of writing BDD style cucumber tests through the GUI bothers me deeply. I think the Rails community has gotten way off course with this approach.Whether you follow my approach, or the GOOS approach, you will still isolate the GUI and the Database from the business rules. You will still create an architecture where the GUI and Database are plugins. The only difference is whether, in the course of an iteration, you are moving from the outside in, or the inside out.While that difference in direction creates almost no difference in the structure of the application, it creates a _profound_ difference in the structure of the tests. Inside out tests have a _lot_ fewer mocks. Outside in tests are _dominated_ by mocks. When you code from the inside out your tests depend on return values and state. When you code from the outside in, your tests depend more on mocks that spy on the way your system work. Inside out tests depend on results. Outside in tests depend on mechanism and algorithm.
Mock objects are a popular tool for isolating classes for unit testing. When using mock objects, your test substitutes its own object (the “mock object”) for an object that talks to the outside world. The mock object checks that it is called correctly and provides a pre-scripted response. In doing so, it avoids time-consuming communication to a database, network socket, or other outside entity.Beware of mock objects. They add complexity and tie your test to the implementation of your code. When you’re tempted to use a mock object, ask yourself if there’s a way you could improve the design of your code so that a mock object isn’t necessary. Can you decouple your code from the external dependency more cleanly? Can you provide the data it needs—in the constructor, perhaps—rather than having it get the data itself?Mock objects are a useful technique, and sometimes they’re the best way to test your code. Before you assume that a mock object is appropriate for your situation, however, take a second look at your design. You might have an opportunity for improvement.
"So I actually don't use mock object very often. I tend to avoid them. It is the classicist mindset, as opposed to the mockist mindset. some people like to go top-down using mocks to drive the implementation as they go. I don't claim to understand that approach very well, but it is very popular and I think the preeminent book describing that would be Steve Freeman and Nat Pryce's GOOS, I have heard many good things about that book though I haven't read it yet. So if you want to see more of the mockist approach, which is more of a top-down approach that stubs out each layer using mocks, check out his book. Steve Freeman and Nat Pryce were the ones of course who invented the mock object technique, so they really know their stuff. I have used mocks before, never really got it, I need to read their book to understand how they do it, but I prefer not to use mocks....I'm sure there will be an occasion when we actually do want to use them... "
"Now there is this thing called mockist versus stateist. If you ever heard this discussion before, about people who use mock objects versus people who dislike mock objects: it is absolute rubbish..that thing doesn't really state what is the actual problem here. A lot of people who do state-based testing, where you test the state of your code, don't want to check interactions at all of something, they don't use interactions for designing, they would treat something as a black box. And people who use mock objects, apparently use mock objects for everything, just assert that, assert this. That is also not true. So whenever you hear this, yuo should understand that really, what we are talking about, is that mock objects are important, but not most of the time. Maybe 5% of your tests should have mocks, but nothing else. When we talk about a unit of work, it could be one method, it could be a class, it could be mulitple classes, working together, but the end result of invoking one public method somewhere should end up either with a return value, or a noticeable state change, in that the system's behavour is different, by invoking something at the same level, or calling a thrid party out of our control, or out of the unit's control. And people usually say that mockists will actually call anything a third party, so instead of calling a third party, you will call any party and you will prove that, and that is not true. So in that regard, mock objects are actually not a bad thing, but they have to be used in moderation."
At about 27 minutes in he talks about the book Growing Object-Oriented Software Guided by Tests, and briefly covers a few subject that developers using mock objects will find interesting. Here is an excerpt with a couple of his statements:
"So in a way, mock objects can be used to design communication protocols, interactions between objects, but this can also be very tricky, because if you use mock objects to define communications between objects that are really part of of the same coherent unit/context, that are just internally talking with each other, that are not really doing anything specific 'outside' then you might actually create very brittle tests. … When I first read the book I had problems with that, because they used mocks to define communication protocols, I believed that the more you use mock objects, the more brittle your tests are….So I actually spoke with Steve Freeman (coauthor of the book), and we both actually agree that not everything should have a mock object. Not every communication should be actually checked, but only communications from outside the unit of work.
…
Mock objects are important, but not most of the time. Maybe 5% of your tests should have mocks, but nothing else. …Mock objects are not a bad thing, but they have to be used in moderation."
what changed from 1st to second edition of AOUT? http://www.techdays.ru/videos/6832.html
"Today we are going to discuss everything that I got wrong in the first edition of my book the Art Of Unit Testing, which is quite a lot, so I actually went to goodreads.com and I marked my book as just 3 stars, cause it is not that good now. The second edition fixes a lot of the problems that I had in the first book. So this talk is going to be about everything that I learned in the more than 6 years since I started writing the first edition of the book.I learned a lot of things. What does unit testing mean? We'll talk about the unit of work. We'll talk about......so one realisation that I have had is that usually you don't need mock objects in your tests, usually you try to have the first two types of tests:return values, and system state change, and if you can get those two and not have mock objects at all in your tests, you are in a good position,because mock objects by definition test internal interactions, and so by definition those tests are less maintainable, and they are harder to read, andthey are longer, so maybe 5% of your tests should have mock objects in your tests, but not all of them, so if all your tests have mock objectsand you assert against things just because you can, you are doing too much, you should stop, please, for your own sake, because in five years youare just going to rewrite the system anyway...it is just going to cause a lot of pain."...[here he goes over the idea of the three types of unit test described in http://osherove.com/blog/2012/5/15/what-does-the-unit-in-unit-test-mean.html]...Because there are three types of unit tests, I updated the naming convention, because the convention I used was just the name of the method that's beingtested, but that is no longer true: it is the beginning, the entry point to the unit of work that is being tested. So any unit of work starts with apublic method somehow...so that is the beginning of the name of our test, and the middle part is the logical action or the input, or the actionOrInputdepending on the 3rd party, and the end is based on the type of end result...1) UnitOfWork_Input_ExpectedOutput e.g. Addition_PositiveNumbers_ReturnSum2) UnitOfWork_LogicalAction_ExpectedChangeInBehaviour e.g. Addition_WhenCalled_ResetsTheNextSum3) UnitOfWork_ActionOrInput_ExpectedCallToThirdParty e.g. Addition_NegativeNumbers_CallsLogger
If we test at too large a grain, the combinatorial explosion of trying allthe possible paths through the code will bring development to a halt. Worse,some of those paths, such as throwing obscure exceptions, will be impractical totest from that level. On the other hand, if we test at too fine a grain—just at theclass level, for example—the testing will be easier but we’ll miss problems thatarise from objects not working together.How much unit testing should we do, using mock objects to break externaldependencies, and how much integration testing? We don’t think there’s a singleanswer to this question. It depends too much on the context of the team and itsenvironment.
[Test] public void ShouldOpenConnectionThenSendAndThenCloseItWhenAskedToSendPacket() { //GIVEN var connection = Substitute.For<Connection>(); //Connection is an interface var sending = new Sending(connection); var packet = Any.Instance<Packet>(); //WHEN sending.PerformFor(packet); //THEN Received.InOrder(() => { connection.Open(); connection.Send(packet); connection.Close(); }); }
One of the things we realised when writing Growing Object-Oriented Software, is that the arguments about mocks in principle are often meaningless. Some of us think about objects in terms of Alan Kay’s emphasis on message passing, others don’t. In my world, I’m interested in the protocols of how objects communicate, not what’s inside them, so testing based on interactions is a natural fit. If that’s not the kind of structure you’re working with right now, then testing with mocks is probably not the right technique.
This post was triggered by Arlo Belshee’s on The No Mocks Book. I think he has a really good point, buried in some weaker arguments (the developers I work with don’t use mocks just to minimise design churn, they’re as ruthless as anyone when it comes to fixing structure). His valuable point is that it’s a really good idea to try setting yourself style constraints to re-examine old habits, as in this object-oriented calisthenics exercise. As I once wrote, Test-Driven Development itself can have this property of forcing a developer to stop and think about what’s really needed rather than pushing ahead with an implementation.
Arlo Belshee
I agree that the optimal testing design will have a number if test doubles involved. It may even, depending on the code, have some full behavioral mocking. However, most usages of mocking that I see in typical code end up being duplications of the implementation expectations of the depended on code. Thus, a change in the dependency requires remembering to update all the tests for dependents.
This is time consuming at best, and error prone at worst (if I miss a dependency).
I also find that any general purpose tool, applied liberally, reduces thinking. This decreases thinking about alternate designs. I’ve had to learn dozens of design techniques in order to change from code easily tested with stubs to code easily tested with just inputs and outputs.
I really like the tell, don’t ask design style. I use it often. And when I use it, I test it with mocks. I just also use a bunch of other styles in other places. Sometimes I need to encapsulate activity and expose data (compiler passes, for example). Sometimes I need to encapsulate both data and communications, and expose behavior. Sometimes I just want to expose who is listening to what when, and don’t want to execute behavior or examine state.
Overall, I use test doubles now and then and full mocks rarely. They are critically useful in the right places. I just always make sure that I’m not using them where a better design would do.
steve.freeman @arlo Thanks for responding. I too have a line about going too far and then pulling back, “if you haven’t overdone it you don’t know where the boundaries are.”Yes, there’s a lot of dreadful use of mocks out there–and of procedures, data structures, objects, functions, variables, etc. So it’s important to distinguish between disagreement with a technique and an instance of its use (after all, the title is “No Mocks”).I’d really like to see you follow through with more refactoring of the example to see where it takes you. I understand your intent, but I wonder how it would pan out.
steve.freeman For me, the way of thinking. Done properly, I don’t believe that mocks are any more (or less) constraining than any other unit test approach–sometimes the right thing to do is to delete tests. I think I’d argue that mocks that got in the way are still a symptom of design problems and that just deleting them is not really addressing the issue.
One question I get asked by teams adopting Test-driven Development is "should we use the 'Classic' or the 'London school' of TDD?"Before I set out my answer, I should probably explain what I think is meant by "Classic school of TDD" and "London school of TDD".If you've read books like Kent Beck's excellent Test-driven Development By Example, you will be familiar with the "Classic school". What distinguishes this school is the emphasis on a technique called triangulation....The London school's definitive text is Growing Object Oriented Software Guided By Tests by Steve Freeman and Nat Pryce....Our goal here is reliable code and good internal design, which means that both schools of TDD are at times necessary - the Classic school when we're focused on algorithms and the London school when we're focused on interactions. Logic dictates that software of any appreciable complexity cannot be either all algorithms or all interactions (although, to be fair, I have actually seen applications like these - and wish to never see such horrors ever again).This is why my TDD training courses have one day of "Classic TDD" and another day of "London school" TDD. It's not an either/or decision.
Looks like it's time for Nat to repeat his rant :)The two "schools" of TDD are really addressing different aspects of a codebase, neither is sufficient on its own....Actually, there are some differences between us and Kent. From what I remember of implementation patterns, in his tradition, the emphasis is on the classes. Interfaces are secondary to make things a little more flexible (hence the 'I' prefix). In our world, the interface is what matters and classes are somewhere to put the implementation.Like any technique, there is a huge "misunderstood" school. The "London" tag is an accident of history, but we did have a strong local group at the time that led to a number of innovations.
Hemal