
Using No Mocks to Improve Design (2012) - edward
http://arlobelshee.com/the-no-mocks-book/
======
1_player
One thing I've learned from using immutable, functional languages (Elixir) is:

"Don't tell your computer what to do, tell it how to transform data"

While it may seem obvious, it's been a revelation for me and it has totally
changed how I write code, and especially how I test it.

In the example they read some command line arguments, read an input file, and
process it. The natural instinct it to implement it exactly as it reads.

You can't do that on a functional language, you need to reframe it into:

1) Transform command line arguments to some symbol representing the action to
execute.

2) Transform a file name into a string representing the file contents.

3) Transform a string (file contents) into some data structure generated by
processing it.

For example, in an Elixir pseudocode:

    
    
        command = parse_command_line(argv)
        case command do
          {:process, file} ->
             contents = read_file(file)
             output = analyze(contents)
    
          _ ->
             print_usage()
        end
    

Now each of these functions can be tested by itself and doesn't require
mocking. They are just pure functions transforming input arguments into
something else.

But there's no error handling: Elixir would solve that very elegantly with
both pipelines and pattern matching:

    
    
        parse_command_line(argv)
        |> read_file
        |> analyze
        |> output_result_or_error
    

Each of these functions accepts and returns a tuple of {output, error}, and,
if "error" is set, we just don't do anything but pass it down the pipeline.
Basically the Either monad from Haskell.

Errors-as-values are much better than exceptions for this exact use case.
Exceptions are hard to test, to replicate, to inject, while in this example
you can just test error handling by.. passing an error value to the function.

~~~
kwhitefoot
Agreed, but I'd like to point out that you can write in functional style in
pretty much any language. Most of my professional life involves VB.net which
is mostly object oriented (for some value of object oriented) and procedural
but I try to write as much of the code as possible as functions with no side
effects. The biggest problem is convincing my colleagues to do the same.

------
a-priori
I really don't understand why this post says that "mocks are a smell".

For the last few years I've been mostly a Java developer and I've settled on a
style of using a very mock-heavy testing style focusing on unit testing the
interactions it has with other objects in the system. I use Mockito
([http://mockito.org/](http://mockito.org/)) to automatically generate mock
objects from a class/interface.

When you combine it with dependency injection you get a testing workflow where
you can fully isolate the unit-under-test from the outside world and
rigorously verify its interactions with its peers.

------
adrusi
This article starts with the assumption that testable code is better code. Its
primary point against mocks, that mocks let you write tests without changing
your code, is dependant on that assumption. Without the assumption, there is
no reason to believe that changing your code for the sake of tests is good or
bad.

I claim that that assumption is faulty. Code being easily testable indicates
that it follows a certain set of good rules of thumb for writing good code.
You're unlikely to find 500 line functions that encompass half of the
program's logic. You're likely to find well delineated inputs and outputs of
functions. The vast majority of the time, these are _good things_ , and it's
tempting to say that since writing testable code gave use these good thing,
that only testable code is good. Or maybe you'd rather not leap that far —
perhaps you'd rather only claim that as a rule, code should be written to be
testable.

It's not that it's an unreasonable rule. I'd be inclined to embrace it for
novice programmers, since their judgement for what is and is not good code is
lacking and they have to write tests regardless. Their code will probably come
out better if they write for testing than if they try to use their best
judgement. But once a programmer develops their judgement, they must use it.
Programming is an art — a controversial statement, I know. It is not an art
like music or painting or writing, it is more like architecture. Programming
is about communicating simultaneously with humans and computers. Good code is
that which achieves its technical requirements of reliability, performance,
security, etc. in the way most accessible to a human. Good architecture is
that which achieves its technical requirements of structural stability, cost-
effective layout, energy efficiency, etc. in the way that best meets the needs
of the people who will be seeing and using the space.

There's nothing about testing in that definition. Optimizing code for tests is
like optimizing a building for government inspectors. Yes, making all of the
piping and wiring easily accessible via a hatch in the wall is a good thing
because it will allow easier repairs and upgrades, but splitting the toilets
into three different parts connected by rubber hose so that the inspectors can
verify that the flush mechanism and the trap and what-have-you are all working
properly independently would be inadvisable.

I'm not going to attack the examples presented in the article because they are
clearly a strawman. They serve to demonstrate the ideas the author sponsors
rather than as a case study in how the author is right.

~~~
mariodiana
When we use the term "art," we're adapting the Latin term "ars" to English.
"Ars" also translates as "craft." Using "art" probably worked well 100 or more
years ago when most educated people had taken Latin and, thus, had more ready
access to such nuances, but today I think "craft" is the better word.

"Programming is a craft" communicates the idea that its practice is not
constrained to a set of hard and fast scientific principles; but, rather, that
it's an endeavor that requires much experience to fit theory into practice,
and that there are even certain aesthetic components to its practice.

Programming is a craft in the same way that non-fiction writing -- or
architecture, to use your example -- is a craft. We can of course say "art,"
but then we get distracted by the thought of men wearing hair buns and skinny
jeans.

~~~
adrusi
"Art" and "craft" aren't synonyms though. Art is a subset of craft. A craft
can relatively easily be defined as something which is produced by applying
skill.

Defining art is notoriously hard, but the most terse definition offered by
anthropology is the "transformation-representation" definition, wherein art is
anything that alters the beholder's experienc of an idea by way of its
representation. This means that all communications with a human in a way where
the representation is considered (so excepting casually chatting with someone)
are an art. Source code communicates with humans using a representation that
mind-numbingly explicit, and a person's mental-model of how a procedure or
algorithm conceptually functions is transformed by viewing source code.

So I agree that describing programming as a "craft" is more generally useful,
in the specific case I addressed in my comment, it is the respect in which
programming is an _art_ that is relevant.

------
andy_ppp
I've been thinking about this for a while; change code vs mocking. I think
what I've been looking for is a justification for doing the first and I agree
with the design argument here; it feels strange reaching into underlying code
so supply plugable paths for not just tests but all code to be able to change
the part that you were about to mock. It'll lead to a more flexible system
overall and one that the tests are more explicit and use less magic.

------
mikekchar
I'll give my 2 cents (which may or may not be worth it ;-) ).

Mocks are useful when you are doing TDD. At some point you have to build
something which will need several collaborators, but the collaborators have
not been built yet. You're not sure yet what final shape the design should
take, so it's great to build an object, mocking the collaborators. You can
then build the collaborators. This allows you to refactor your design easily
and quickly when you are in this early stage. It's easy to change the
interface of an object that hasn't been written yet ;-) Then when you
implement the mocked object, it's easy to write because you just implement the
bits that were mocked.

At this point, I tend to replace my mocks with real objects. The reason for
this is almost exactly the same reason for using mocks initially (ironically):
mocked objects make your design hard to refactor. If I change the interface
for an object, then anything that has mocked that interface needs to have its
tests updated. This is not hard, but is crappy enough that it stops people
from refactoring their interfaces. When you are using real objects all the way
through, you often only need to change the production code and the unit tests
for the object whose interface you changed.

Real objects in tests are also really useful for documentation purposes. When
you use real objects you have to show how to build the objects and how all the
collaborators work. Some people tend to think that this is "integration
testing" and should not be done in unit tests. I disagree fairly strongly with
that idea, but I would be remiss if I didn't acknowledge that some of the
anti-real-objects-in-unit-test people are pretty smart people ;-) Obviously
real objects are safer to use as there are ways to confuse yourself with mocks
so that you don't actually test anything meaningful.

The main downsides with real objects is: sometimes you have to build the world
to make them, and sometimes the real object has some external dependency which
makes testing with them infeasible. The first issue is often a sign of poor
design and should be rare -- but of course life happens. The second issue is
usually not something you can control. In these circumstances, I will prefer
to use mocks. In some circumstances you end up having to make mocks of mocks,
or reaching down into structures you shouldn't know about. In those cases I
prefer to use fakes.

All of these techniques are valid and useful techniques. In any non-toy
problem you will have to use all of them to have a good test system. The trick
is knowing when you should be using each. That requires some experience.

