Specification By Example - BDD for Pragmatists


    On Habré quite a lot of references to BDD. Unfortunately, the articles I read did not give me the answer to the question “why do I need all this?” The answer came from an unexpected angle. When I seriously took up the issue of acceptance testing automation, I came across the book Gojko Adzic (I’m not sure about the transcription, so I didn’t translate the author’s name) Specification By Example .
    Reading it, I did not get tired of being surprised: each new chapter described the bumps that I had typed in my personal experience, and offered solutions similar or better than those to which I came to myself by trial and error.

    This article is the first in a series of "BDD for Pragmatists." It describes the key elements of the most effective, in my opinion, commercial software development process in modern conditions. Two sequels will be devoted to working with SpecFlow and automation of acceptance testing.


    For the product to “take off” in the conditions of the modern IT market, we, developers, need to achieve two goals. Make the right product (exactly what the market wants to buy) and make the product right (without spaghetti code, crutches and other technical debt).

    The right product and the right product are not the same thing!


    Failure to fulfill the first condition will lead to a financial collapse, which means that it is likely to close the project. Failure to comply with the second - to the fact that one day we will find ourselves waist-deep in the g ... code. Probably, you do not need to explain all the charms of legacy-system support without tests and specifications.

    Specification By Example is a process that allows you to regularly achieve both points. The process is based on agile, tdd, bdd, continuous integration and test automation.

    Key elements of Specification By Example :
    1. Highlight the main thing (deriving scope from goals)
    2. Together constitute specification (specifying collaboratively)
    3. Give examples (illustrating using examples)
    4. Clean specification (refining the specification)
    5. Implement automation testing without changing specification
    6. Embed test execution in the build process and develop documentation (validating frequently, evolving a documentation system)



    Highlight the main thing (deriving scope from goals)


    The vast majority of people are able to formulate what they really need. In most cases, you will encounter the fact that the client does not formulate the requirements in the form “I have a goal, how can we achieve it?” , a: "make me a child prodigy, like a neighbor, but with mother-of-pearl buttons . " The client will tell you how he sees the solution to the problem, and dive deeper into the implementation details.

    Here lies the first and most important mistake . Usually, the same problem can be solved in several ways, and in the form of requirements we are already asked to implement one of the possible solutions. But not all solution yogurts are equally useful effective.
    Our task, as engineers, is to offer a solution better and cheaper than the client asks. This principle was formulated by Henry Ford:
    "If I asked people what they want, they would ask for a faster horse."

    Fighter F-16 - one of the most successful fighters in the history of the US Army. The initial development requirements were to reach Mach 2-2.5, which in combination with other requirements made the development and production of such an aircraft very expensive. Harry Hillaker - the leading designer of the F-16 - clarified why this requirement of speed is so important, and received the answer: "the fighter must hit the road if it gets really hot . " Hillaker proposed an alternative solution and designed a fighter, superior at that time to others in maneuverability.

    More than 30 years have passed, and these fighters are still producing. 4400 aircraft sold in 25 countries. It is on the F-16 that fly in most Hollywood films: from Independence Day to Transformers ... F-16 still cannot reach a speed higher than Mach 2.
    F-16 turned out to be so successful because the engineer offered a better solution and cheaper than the client requested.

    Pay attention to the goal, and do not go straight to the decision. There are many ways to achieve the goal.



    How to highlight the main thing


    Albert Einstein once said:
    “Formulating a problem is often more important than solving it.”

    Before coding, make a specification. Yes, we, the developers, do not like pieces of paper, formalism and multi-page Talmuds that no one reads in the end. But think about how often you cursed the one who made the requirements and acceptance criteria. Take responsibility and make the specification yourself in the form in which it will be convenient for you to work.

    Here are some examples of unsuccessful items that fell into the specification:
    • All pages should be displayed in 0.1 seconds. The word “everything” should be avoided in the specification in principle. Yes, the home page should open as fast as possible. But you will spend weeks or months trying to get into this framework a summary report that runs once a year. Such rare and resource-intensive operations can take a long time. Nothing wrong with that.
    • The user-interface should look like OSX. Fashion has spawned thousands of sites with the replacement of native controls, and with them the pain of thousands of typesetters and web developers. Let the interface look like OSX on the poppy and in the Safari browser. On other systems, leave the native controls.
    • There should be a flash banner on the main page of the website. Perhaps this banner does not need a flash at all, and everything can be done using html / css / javascript, and you already have a similar module out of the box. Avoid overclarification of technological aspects in the specification.


    Use user-stories


    User-stories are one of the best ways to understand what is really needed. User-stories can be formatted a little differently, but they must contain 3 points:
    In order to - why?
    As a - who?
    I want what?

    Having worked through each such story, you will be able to understand whether you really need what they are asking for, or if there really is a better solution to the problem that no one just thought about. If I want is already formulated, but As a and In order to are not, this is an occasion to think. Perhaps you are going to develop functionality that nobody needs.

    This is what spam mailing user-stories might look like online store loyalty programs (I apologize to readers who do not know English well, but I am convinced that the documentation should be in English):
    • In order to be able to do direct marketing of products to existing customers,
      As a marketing manager
      I want customers to register personal details by joining a VIP program.
    • In order to entice existing customers to register for the VIP program,
      As a marketing manager
      I want the system to offer free delivery on certain items to VIP customers.
    • In order to save money,
      As an existing customer
      I want to receive information on available special offers.


    Specify collaboratively


    A specification drawn up solely without a team is the second big mistake and potential space for misunderstanding and subsequent edits. Instead of relying only on one specialist, involve the entire team in the preparation of the specification. If you work on scrum, a rally is a great time to do this.
    Developers better understand the infrastructure and know the technologies that can be applied to solve the problem. QA specialists will indicate in which places errors may occur. Product-owner is an expert in the subject area. All of this information is useful for compiling specifications. Collaboration allows you to:

    • Better deal with the problem and find the best solution.
    • Make the whole team understand the new requirements equally
    • Involve all participants in the process


    At the planning stage, it is better to connect the entire team. After the main field of work is clear, pair work or small meetings for 3-4 people are more effective in order to clarify difficult points.


    Give examples (illustrating using examples)

    Natural languages ​​play a trick on us. They leave room for interpretation, misunderstanding, and sometimes require knowledge of the subject area and / or specific jargon in order to understand what is at stake. A slight misunderstanding can lead to deadlines and a large number of edits or even rewriting entire modules. Instead of formulating requirements long and painfully, describe them with examples. Together with the client you can find key examples . The help of developers and testers is simply invaluable in this case, because they can identify potential problem areas and technical limitations in advance.

    Here's what a collaboration process on a specification might look like. For example, take the online store "Horns and Hooves."
    • Barbara - customer representative
    • Vasya is a developer
    • Olya - QA

    Barbara: So, we have concluded an agreement with the publishing house ABC Press that we will deliver books purchased from us for free.
    Vasya: All over Russia?
    Barbara: No, what are you. Only in Moscow.
    Olya: And in our admin area is the correct publishing house filled everywhere? Varya, you can give us a complete list of books, I'm afraid we can get it wrong, in the database the “publishing house” field is a line.
    Barbara: Yes, of course, there are not many books there.
    Vasya: How many books can we deliver for free? Do we have any restrictions: 5-10? What happens if books from other publishers are in the basket?
    Barbara: It doesn’t matter, as long as there is at least one book in the basket, delivery is free.
    Vasya:Well, what happens if a customer orders a book and a fridge? Such delivery will be expensive ...
    Barbara: Yes, we somehow did not think about it. Let’s write down and clarify, and we will discuss this in a week, is there enough information to start work?
    Vasya: Of course, I'll start sketching the architecture.
    Olya: And I’ll check what's with the publishers.
    In 3 days.
    Barbara (by phone): We talked and decided that free shipping would be available only for books. If there is anything else in the basket, only normal delivery. And we decided to limit the number of books from above to no more than 10.
    Vasya: Ok.

    As a result, we get key examples :
    Customer typeCart contentsDelivery
    VIP1 bookFree
    VIP10 booksFree
    VIP11 booksStandard
    Regular10 booksStandard
    VIP5 washing machinesStandard
    VIP1 washing machine, 5 booksStandard


    Refining the specification


    Through a joint discussion, you will be able to achieve a common vision of the goal. You can compare the previous stage with the fact that you mined a diamond. A diamond in itself is quite valuable, but after processing its value will increase many times over.

    During the discussion, each team member will pull a blanket over himself.:
    • Business representatives tend to pay too much attention to UI (because they are not specialists in the technical field, UI is the only thing they can “touch with their hands”).
    • Developers tend to delve into implementation details: which framework to choose and which technology to apply.
    • Testers will be paranoid in their search for bugs and vulnerabilities.

    Thus, the initial version is likely to be contradictory and redundant.
    The purpose of this stage is to separate the grain from the chaff and provide the right amount of detail. The specification should become a single document for:
    • Acceptance Criteria
    • Acceptance tests
    • Future regression tests


    In order to minimize the chances of misunderstanding, we will write the scripts in terms of Given When Then . If you are not familiar with this form of writing, remember the math lessons at school: Given, Find, Solution . It's about the same here.

    Given - initial context (precondition)
    When - event (which is a script trigger)
    Then - the result we want to get

    For our example with books, it will look like this:
    Feature: Free delivery
    	In order to save money
    	As a VIP customer
    	I want the system to offer free delivery on certain items to me
    Scenario: Free delivery
    	Given I am a VIP customer
    	And I am on product detail page
    	And There are only books in my shopping cart
    	And There are <= 10 books in my shopping cart
    	And I have added 'ABC Press' book to my shopping cart
    	When I press 'Go to checkout' button
    	And I have chosen 'Moscow' in 'Ship To' dropdown
    	Then I can choose free delivery
    

    This form of recording, on the one hand, remains readable for mere mortals of non-technical specialists (managers, client representatives, business analysts), and on the other hand, it is strict enough to avoid ambiguity.
    We can modify the script a bit to please QA.
    Scenario Outline: Free delivery
    	Given I am a VIP customer
    	And I am on product detail page
    	And There are only books in my shopping cart
    	And There are  books in my shopping cart
    	And I have added 'ABC Press' book to my shopping cart
    	When I press 'Go to checkout' button
    	And I have chosen 'Moscow' in 'Ship To' dropdown
    	Then  is available
    	Examples: 
    	|bookQuantity|deliveryType|
    	|5 |Free        |	
    	|10|Free        |
    	|11|Standard    |
    

    In this case, the script can be used as a list of test cases.
    To write this script, I used SpecFlow, a solution for the .NET platform. There are similar tools for Java, Ruby, PHP.

    It's not for nothing that I focus on the instrument. How exactly to write tests using Given When Then is a too extensive topic, which we will discuss in the next article, but for now we will restrict ourselves to basic information. Tests are created in a declarative style, using the "steps". Note that the parameters from the Examples table will be passed to the arguments of the methods.

    namespace ProjectName.Specification
    {
        public class FreeDeliverySteps
        {
            [Given("I am a VIP customer")] 	
            public void GivenIAmVipCustomer()
            {
               throw new NotImplementedException();
            }
            [Given("I am on product detail page")]
            public void GivenIAmOnProductDetailPage()
            {
                throw new NotImplementedException();        
            }
            [Given("I have added \'ABC Press\' book to my shopping cart")]
            public void GivenIHaveAddedAbcPressBookToMyShoppingCart()
            {
                throw new NotImplementedException();    
            }
            [Given("There are only books in my shopping cart")]
            public void GivenThereAreOnlyBooksInMyShoppingCart()
            {
                throw new NotImplementedException();    
            }
            [Given("There are (.*) books in my shopping cart")]
            public void GivenThereAreBookQuantityBooksInMyShoppingCart(int bookQuantity)
            {
                throw new NotImplementedException();    
            }
            [When("I press 'Go to checkout' button")]
            public void WhenIPressGoToCheckoutButton()
            {
                throw new NotImplementedException();    
            }
            [When("And I have chosen 'Moscow' in 'Ship To' dropdown")]
            public void WhenIHaveChosenMoscowInShipToDropdown ()
            {
                throw new NotImplementedException();
            }
            [Then("Then (.*) is available")]
            public void ThenDeliveryTypeIsAvailable(string deliveryType)
            {
                throw new NotImplementedException();
            }
        }
    }
    

    You can read more about BDD on the website of the founder of this paradigm .

    Implement automation testing without changing specification


    The completed specification will serve you both as a technical task for the development of functionality and test scripts to verify the correctness of the work. Do not put off test automation for later. Tests will detect errors at an early stage of development, which means you do not have to redo the functionality again and again.

    If when the requirements change in the future, you won’t need to figure out which tests are no longer needed. Since the requirements have changed, then the specification also needs to be changed.
    The tests that covered the part of the functional code that “fell under the knife” will either turn red or stop working altogether (go into Ignored status ), and the process will need to be repeated first: write tests, rewrite code, verify that all tests pass.

    With this organization of work, you will change the specification and tests in one place. That is, the specification itself will become executable .

    Embed test execution in the build process and develop documentation (validating frequently, evolving a documentation system)

    A lot has been written about the benefits of Continuous Integration on Habré. We will review this practice from our bell tower. So, we have an executable specification (a specification with examples and related automated tests). Unfortunately, most of the team still can not "feel" the result of the work done. Managers will not install the IDE, hung with plug-ins, and deploy the entire system on their machine.
    You need to integrate test execution into your build system (if there is no build server in the organization, maybe this is an occasion for it to appear?)

    Once the tests are performed regularly, you will notice that automated tests are the most reliable source of information about the state of affairs in the project at the moment. The graph below shows an example of a successful development iteration. On the first day, a small part of the acceptance tests was written and all of them are red (which is understandable, because we do not have any functionality yet). Gradually, the number of tests increases to cover the entire development scope. In parallel, the number of green tests is growing - they began to develop functionality.

    The number of green tests is the only reliable criterion for evaluating what has already been done and what remains to be done.
    Unfortunately, practice shows that developers, albeit with good intentions, can report “yes, yes, tomorrow it will work” for months. The schedule will not lie:The feature is ready when all of its tests are green .

    Too many red tests, and release soon? Most likely, measures need to be taken, otherwise the functional will not be ready on time with the proper level of quality.

    With the development of the product, the specification will grow, and the requirements will change after the requirements of the market. Even such a specification will have to be maintained and structured so that all information is quickly available. I am ready to pay this price for the confidence that every next release will take place without deadlines, overtime, and the product will work like clockwork.

    Why am I

    The world around us is changing very quickly. And now the world of business and development has turned to each other, if not face, then at least a half turn. But the abyss of communication has not yet been completely overcome.
    I started implementing Specification By Example practices much earlier than reading the book. Many of the recommendations described come with experience; much can be gleaned from other sources. It is really valuable that Gojko Adzic was able to organize this information and propose a process.

    My experience fully coincides with the experience of the author: projects that implement the principles of Specification By Example are developed faster, and the number of bugs and edits is reduced.
    I hope the article will be useful to young teams who are in search of a suitable process.

    References


    Also popular now: