Mock Terminology

By Jeff Langr and Tim Ottinger
Font: BrownBagLunch

Well, the title of this post is just wrong. The generic term for "things we use in testing to emulate production behavior" is test double, not mock. The casual programmer may bandy about the term mock when they mean test double, but it is technically incorrect and may lead to misinterpretation. We've never seen it make much of a difference to the end result of a programming conversation, but there are distinct definitions for the various implementations and uses of test doubles.

The term mock object stems from a 1999 paper by Tim Mackinnon, Steve Freeman, and Philip Craig, "Endo Testing: Unit Testing With Mock Objects." The authors' simple definition: "a substitute implementation to emulate or instrument other domain code." Mocks, or whatever you might call them in 2010, still serve the same purpose.

Use of mock objects in TDD circles grew dramatically over the next several years. Debate grew dramatically, too. The community debated about (a) whether or not to use them at all, (b) in what situations they were most appropriate, and (c) whether or not to use one of the mock tools that were starting to proliferate and pro-create.

In 2006, Gerard Meszaros published the book XUnit Patterns, which enshrined a handful of nuanced terms for the various kinds of test doubles. These terms had been shopped around in various agile forums for some time leading up to publication of the XUnit Patterns book. Today the taxonomy is commonly accepted by programmers and mock frameworks alike (oops, but "mock frameworks" itself is a misnomer, as these frameworks usually support all sorts of test doubles, not just mocks).
  • Test Double is the generic term, the phylum for all our species of testing dopplegangers.
  • Stub - An object that returns a specific, fixed value to the system under test (SUT). Stubs are usually constrained to a small subset of methods defined on a collaborating class. "When someone calls the price method, return the value 9.99."
  • Fake - An object that completely emulates its production equivalent. The classic example of a fake is a lightweight, in-memory "database" object that allows for simple, fast emulation of a relational database interface.
  • Mock - An object that self-verifies. A mock asserts that information sent to it is as expected. A test that uses a mock defines and verifies this expectation.
  • Partial mock - An object that contains a mixture of production method implementations and mock method implementations. Partial mocks are generally used when you need to emulate non-existent behavior (i.e. abstract methods) or troublesome behavior defined on the same class you are testing, something that might indicate questionable design.
  • Spy - An object that simply captures messages sent to it, so that the test can later verify that the SUT interacted correctly with its collaborator.
These terminological differences may on occasion be useful but are not worth arguing with any real passion. We suggest learning the differences as a means of avoiding time wasted with arguments, using a formulation along the lines of "I'm sorry. Of course I meant spy, not mock. Now, look on line 47..."

So, where do we stand on the debate? (a) Yes, use test doubles (b) when you must, (c) and use a tool if it makes things easier or clearer. Next time, we'll talk about more important things, such as what pitfalls to avoid when working with test doubles.

Organizational Objections To Agile Process

By Jeff Langr and Tim Ottinger
Font is Andrew Script

It is no surprise that organizations struggle when attempting to transition to agile methods. As with any new venture that threatens the status quo, the list of objections is long and varied. In developing this card we collected over a hundred typical sentiments and grouped them into about a dozen categories--too many for a single card. In keeping with our personal vows of brevity, we present here the first card: Objections borne out of organizational and cultural circumstances. We will present reasons that stem from individual belief systems and biases separately.

In order to help transitional consultants and rank-and-file people who are struggling, we provide commentary and counter-arguments.
  • "It Can't Work Here" There is a common assumption that Agile methods require a special set of initial conditions. Most companies believe that their own situation is unique, their software uniquely complex, their market position too tenuous, or their management system too inflexible. Given such specialized initial conditions, how could a general-purpose method based on simplicity possibly work?
    Agile is not so much a set of a constraints and rules as it is a framework in which a team can continually discover its own limitations and then derive better approaches. There are few necessary initial conditions beyond an agreement to work together in an incremental and iterative way.
  • "They Won't Let Us" is a special condition of "It Can't Work Here." Agile methods may be deemed feasible and even advantageous among the technical crowd, but may seem counter to the organizational culture and/or management habit. For instance, there may be a competitive personnel evaluation system for staff which frustrates attempts at collaboration. The management might have a strong command-and-control model which prevents self-organization. Perhaps the organization is built upon the concept of a strong lead programmer directing a squad of "code monkeys." Maybe schedule pressures are so great that there is no slack to spend on organizational learning. It may be that the team cannot modify the layout of the office due to union issues or concern for decor.
    Agile methods are advantageous to development and product management since they provide more data about the team's real progress, allow better focus on important features, and require very limited limited overhead to practice. While some aspects of Agile practice are clearly focused on management practices and product management strategies, an increasingly capable team can usually cope with difficult organizational practices in the short term and can win over leaders in the long run.
  • "Guilt By Association" refers to the situation where Agile methods have not been tried, but at first blush seem to resemble other methods or practices that have fallen from favor. Sometimes Agile is associated with uncontrolled "cowboy coding." Other times Agile is perceived as a trick by management to force programmers to work harder. It may be confused with ceremony-heavy consulting-driven methodology. The poor image is often tarnished further by tool vendors hoping to cash in on the latest buzz with tools that are rarely necessary, of limited helpfulness, hard to learn, tedious to use, or even detrimental to collaboration and communication. An Agile conversion project may follow on the heels of other failed "flavor of the month" methodology attempts.
    Agile is a low-ceremony, disciplined way of working built on concepts and ideas that have been successfully applied in the software industry for many decades, and longer in other industries that still embrace these principles today. It is a simple, incremental approach to team software development that requires little tooling beyond a place to sit, a whiteboard, a good supply of index cards, and a few rolls of tape. It would be a shame if an unfair prejudice caused us to miss out on an opportunity to build a truly great team.
  • "Means/Ends Juxtaposition" is a variation on "cargo cult" mentality. A typical non-agile company will have layers of policy and management practices built on strict phase separation, copious up-front documentation, individual accountability, rigidly-defined hierarchical roles, and/or tail-end quality control processes. Artifacts produced by these practices become the primary output of teams, and enforcing mandated behaviors becomes the primary concern of managers, even though neither contribute meaningfully to quality software development.
    An organization attempting to transition to agile may fall into the same trap by rigidly applying so-called agile practices. Numerous teams claim (capital-A) "Agility" because they hold interminable feckless retrospectives, prescribe stand-up meetings that provide only vertical status, or prolong interminable pair "marriages." Stand-ups, retrospectives, and pairing are extremely valuable tools, but only if you are able to align them with agile values and principles.
    To succeed at agile, we must first understand that it is a continual journey of team discovery, and not a rigid set of practices. We must have some sense of where we want agile to take us, and that the journey will reveal unforeseen challenges and opportunities. Agile development is about growth rather than conformity.
  • "Inferiority Complex" The team that ships quality software on a consistent and frequent basis exhibits a high level of confidence. A team that lives with frequent failure and inability to estimate quality of product will exhibit a low level of confidence. An observer may attribute the confidence and ability of the team as an initial condition and not as an eventual outcome of the process, surmising that confident superstars are a necessary initial condition of agile success.
    The Pareto distribution suggests that most teams simply don't have enough star developers to conquer this mistaken understanding of agile. Real concerns over minimizing entropy in a rapidly changing system engender pure fright: "How can we possibly introduce new features without pre-conceiving the entire design? Our code is horrible to start with, and we've tried to write some unit tests, and it's just too hard." A large number of teams ultimately feel they lack the skill to produce anything of value in a short iteration.
    Agile software development is about teamwork, not about superstars. For example, pair programming helps make TDD less difficult (for everyone, superstars and supernovices alike); TDD in turn provides us with the means to safely refactor code; refactoring sustains quality design in an extremely dynamic environment. Likewise, introspection and teamwork allow for continual improvement. Agile is a means to raise the competence of a team and lower the difficulty of working with a code base.
  • "Superiority Complex" is when an organization feels that they have a pretty good handle on things. They have a process that has allowed them to deliver successfully in the past, and regard any change in practice as a step down. Practices like TDD and pair programming are regarded as training wheels for junior programmers, and wholly inappropriate for serious software professionals. They may believe that they have a special gift for up-front design that makes incremental design wasteful and unnecessary. The organization believes they have transcended the need for Agile practices.
    There will always be those who think that the world has nothing left to teach them. If the organization is perfect, there are no flaws to uncover, no waste to reduce, and no improvements for agile to bring. Since agile methods are about doing, measuring, and reflecting on the work, we often find that Superiority Complex is based mostly in wishful thinking and a lack of measurement.
    If, on the other hand, a company has found the techniques that leave Agile in the dust, we would love to learn about and adopt this superior method at work. Agile methods are perhaps not the best methods possible, only the best ones we know as of this writing.
  • "Rejection of Insufficient Miracle" is the tendency to refuse to use a practice which leaves any current problems unsolved. "It's all or nothing, baby!" A team that cannot automate all testing sees little point in automating tests at all. Incremental development does not guarantee that a certain date will be met with all features in place, so there is no reason to iterate. The team must collaborate with other groups which do not work in an Agile way--what is the sense in only part of the company being agile? If we can't guarantee everyone will refactor the code, why should anyone spend the time cleaning up the design? By refusing incremental improvements, the organization rejects the very soul of the Agile way of working.
    Samuel Johnson once said, "
    Nothing will ever be attempted if all possible objections must first be overcome." All software projects, even one that might be hypothesized to be free of technical error, are prone to failure from myriad reasons. All products, teams, and organizations are "insufficient miracles," leaving many of life's problems unsolved. Seeing that we have all gotten out of bed and come to the office, the question is whether we want to try a process that maximizes learning and quality (without guarantees), or an equally guarantee-free process that does not.

We fully recognize that there are teams that will not want to use Agile methods, and we suspect that there are organizations unwilling to modify their practices to accommodate a new style. We suspect that any method will not be successful in such organizations and that abandonment may be a reasonable strategy. As they say, "Change your company, or change your company."

In time, we may discover ways of fulfilling the promises of Agile with other methods. Agile is not the only possible way to improve an organization, even though we have found it to be one exceptional way to improve software-developing companies.

If, on the other hand, an organization is not interested in the kinds of benefits Agile methods promise (teamwork, growth, quality, productivity) we recommend that they become our competitors.

Branch Per Feature

Font: Mechanical pencil for body, Erwin for heading
Sources: Tim Ottinger, George Dinwiddie (via mail list), Jeff Langr

Branch-per-feature is a common SCM strategy in which a branch is created on a central server for each feature that is under development. As a feature is completed, the branch is integrated back into the main development code line.

In some organizations, releases are composed by merging selected, completed features together. This seems quite rational, and can be made to work with enough effort applied in bursts. Every merge creates a unique, new configuration in the system that must be tested for side effects. If the merge has unintended consequences, then one or more features must be modified or the release re-composed without it. As a result, releases become major events in the life of the company with huge testing parties and great schedule risk. These times are frequently called "hell week" or "hell month" depending on the release periodicity.

In addition, the more two lines of code diverge, the harder it is to reconcile their changes. When work is integrated several times a day, it tends to be a fairly trivial effort. When it is integrated only a few times per month, it is rather harder. A few times a year, and it is surprisingly difficult. Likewise, if many branches are held for a long time, each branch will not only diverge from the main codeline but from other branches as well. This effect can make it rather difficult to estimate the effort of integrating branches, which contributes to the nervousness around hell week.

Agile teams integrate continually with great ease and success. Why, then, do some organizations hail branch-per-feature? Teams that use branch-per-feature as regular practice are often compelled to do so because they don't know which tasks/stories will actually be completed by the end of the sprint. It seems easier to hedge bets with version control systems than to tackle the organizational/political problems that frustrate planning and execution of sprints.
  1. Too much WIP (work-in-process) means that too many tasks are undertaken at once. Branch-per-feature helps the team deal with the fact that work is not being finished predictably or reliably within the iterations. Many tasks are reported complete on the last days of the iteration, yet some of those are rejected. Branch-per-feature allows management to decompose and recompose the release at the tail-end as they find out what is actually completed.
    In an agile team, as many as half of the features are done by the midpoint of the iteration and are being tested frequently. Very few changes are actually at risk of being left incomplete. In the best teams, missing an iteration boundary is a rare event, so branched features are unnecessary.
  2. Features are too large if they cannot be completed in a small part of a single iteration. Otherwise the uncertainty prompts the team to fork the code base. The forked code base is more difficult to integrate as it diverges from the original code line. Fear of difficult integrations actually encourages the team to hold isolated branches longer.
    Agile teams instead look to ensure features are small enough to completed within a few days. Small features completed in a day or two rarely require complex merging. Any overlap of effort can be easily coordinated with other team members during stand-ups or ad hoc conversations across the team table, further minimizing the chance that a merge will be necessary.
  3. Structure is poor if changes routinely span many files in many libraries or assemblies (something Martin Fowler refers to as "shotgun surgery"). When a feature's implementation is scattered all over the code base, it is harder to accurately make changes and harder to predict when work will be truly done. This uncertainty drives the team to isolate rather than integrate.
    Agile teams are highly cognizant of the value of simple, SOLID design. They understand that systems exhibiting true cohesion and minimal duplication dramatically lower the need for shotgun surgery.
  4. If there is no way to turn off incomplete features then the team will fear customers stumbling into incomplete sections of code and causing damage to data or additional customer support burden. This force drives developers to develop code in isolation from the main codeline, which of course increases the cost of integration.
    Agile teams view changes to the system holistically, breaking down each new feature as a series of incremental changes to the mainline. They understand that while this requires some level of overhead, it means that merge hell is minimized, and that it demands a better system design. For larger changes, they look to solutions such as branch by abstraction as helping provide both benefits.