Test Driven Development Is To Unit Testing As Interaction Design (IxD) Is To Accidental Design One of the major problems with writing unit tests after the code is that it is very natural to write tests that prove the code works the way it was written, instead of the way it should work. Writing code test-first (via whatever flavor of Test Driven Development) flips that scenario right side up. TDD helps to ensure that code is written to work according to specifications and not the other way around. Similarly, designing and/or implementing a UI after a behavior or process has been coded is a likely to result in a UI that fits the code model and not a model that fits the needed interaction and workflow. This situation must also be flipped right side up - interaction design should be done before, or at least in parallel, with coding. It cannot be left to accidental or incidental happenstance - interaction design must occur with proper interaction patterns and practices in mind. Overlapping IxD and TDD To overlap interaction design and test driven development, there are a few key words that need to be borrowed from interaction design. Fortunately, they easily fit within TDD development techniques and philosophies. Epistemic work is exploratory in nature, or a process of trial and error through research. TDD and interaction design sketching are both epistemic. TDD explores API possibilities and allows easy trial and error to find the simplest implementation for what you need at the time. UI design sketches also allow you to quickly explore interaction designs - whether it's a white board, pencil and paper, graphics design software, or even quick-hack forms layout in IDEs. You can quickly and easily throw away a bad API in TDD and you can quickly and easily throw away a bad UI/Interaction design when you have nothing more than pencil sketches or white board drawings. Pragmatic work is very structured and step-by-step in it's nature, implementing patterns and practices to fulfill what is needed at the moment. Implementing Code after writing unit test and implementing UI after designing around constraints are both pragmatic. TDD is pragmatic in that you only implement what is needed to properly pass the tests that have already been written. Similarly, with previously designed interactions and UI elements, implementation can be easily limited to what is needed for the UI. With epistemic and pragmatic work covering both interaction design and test driven development, it seems that they are a natural pair. An analysis of a user story and it's acceptance criteria will create the unit tests that we need. At the same time, the same analysis can be applied to interaction designs. Additionally, a strong understanding of how a UI will look can have a profound impact on the code that is written, and vice-versa. Therefore, it is natural that interaction design and test driven development are done at just-in-time intervals - before the real work is implemented in the real code and UI platform. IxD As Part of "Done" Despite interaction design being a required part of UI development, not all user stories require a UI. Interaction design may not fit into a swim lane board or be part of every story's "done" criteria. However, interaction design will always be done for any story that does have a UI - it may simply be an accidental or incidental part of the software development process for a team, though. If it's safe to assume that the work will be done, then it is the team's responsibility to ensure that it is done correctly. Don't let interaction design happen accidentally or incidentally in the development process. Set a standard of always including interaction design in the development process, the same way Model View Presenter/Controller is a part of development.
Scott Bellware (who finally started blogging again), is posting a series on Sustaining Capacity in Maturing Agile Software Teams. It's worth a read, and may be familiar territory to anyone who has done Agile and been reading about Lean. In the 3rd Post - Recognizing Entropy - he talks specifically about synchronizing the development testing and QA testing efforts: Development and QA Test Synchronization. Without greater synchronization between development and QA testing, valuable input often comes too late in a development or release cycle to be effective. Test design and test architecture are valuable inputs to development, and software design and architecture are valuable inputs to QA testing. Teams often loose unrealized capacity by not pursuing means to do more development and testing in parallel. This one really hit home for me, and is a shining beacon to the problems that my current team is going to face, soon. We had our first iteration planning and kickoff meeting yesterday, and the whole team was involved - except the testers from the QA department. For reasons that I have no control over (and honestly - they are legitimate business reasons), the QA department is not coming on to the project for another 2 months (at least). Today, the team leadership was discussing how the QA personnel would create their test scenarios, and I brought up the point that creating the test scenarios after-the-fact (after the iterations are done) is going to be very problematic. If a dev team is working from User Stories, and those User Stories are divided along various aspects of behavior, it is very likely that a single screen, workflow, or 'function' of a system is going to be divided into multiple user stories. When the QA team is involved with testing in parallel with the development team, this is not an issue because the QA team will be updating their test scenarios in each iteration. This allows them to keep the scenarios for a given screen, workflow, or 'function' of the system, growing over time. However, since our QA team is going to come into the project at least 2 months after the development team has started coding and testing, the QA team would have to sift through all of the current and previous iteration stories, try and aggregate them down into the screen, workflow, or 'function' of the system, and build test scenarios from there. If there is even a modest number of stories to sift through, this would be a daunting task for the QA team - they would be in a 'catch-up' mode from the beginning of their QA cycle, likely through the end of the project. As a result of the QA team being unable to join our project immediately, we are forced to modify our entire development process and account for the needs of the QA team, after the fact. Essentially, we are creating additional swim-lanes for our stories to travel through, during an iteration: - Backlog
- In Development
- Developer Testing
- Ad-Hoc Testing (BA's, Developers, etc. testing the app)
- QA Functional Spec Documentation
- Done
Swim-lane #4 and #5 are directly affected by the QA team not being able to enter the project immediately, and are causing the rest of the team to lower it's capacity. We (developers and BA's) are now required to do our own ad-hoc testing during the iterations, instead of letting the QA team take care of that; and we (the developers and BA's) are now required to create functional specifications, based on the user stories that are completed, to hand to the QA team once they are on the project. The functional specification documents will need to be updated for any given User Story, before the ticket is considered complete (the functional spec updates have become an acceptance criteria on every User Story). End result - because we cannot get the QA team on our project immediately, and ensure that they are testing in parallel to the developers, we as the development team are losing capacity and building entropy from the very first day of the very first iteration.
Yesterday, I posted a quick thought on code generation, and one of the statements I made is worth re-stating to stand on it's own. In Agile/Lean software development, a single User Story is one piece flow when implemented via TDD, in a Workcell In Lean Manufacturing, one piece flow is the idea that you do not produce anything in batches, but that you produce one product from start to finish per a customer's order. In Lean / Agile software development, this is analogous to an iteration with user stories. An iteration backlog is a customer order - a set of behaviors or features that are requested to be delivered in the current iteration. A user story is the single piece that we want to flow from beginning to end - from the start of coding all the way to acceptance via user testing. In manufacturing, a Workcell is used to facilitate one piece flow through the manufacturing process. In software development, a Workcell is also used to facilitate the one piece flow. The Workcell may be 1 person, pair programming or a three person team. In order for it to truly be one piece flow, though, strict standards such as TDD and automated acceptance tests must be followed. I'm not going to detail the benefits of one piece flow, here (not yet, anyway... another post for another time). I highly recommend that you read The Toyota Way and Lean Thinking for a good understanding of this concept and the benefits it provides.
Until around a year ago, I was an advocate of code generation via CodeSmith. Having marginal "success" with it in the 4 years I advocated for it, I'm now of a different opinion. Code generators, such as CodeSmith, are automated overproduction machines that require prior overproduction, in the form of schema, to be used Micro code generators like as Resharper, are much closer to JIT machines when lined up in one-piece-flow processes, such as Test Driven Development ... If you had told me, 2 years ago, that I would make these statements today, I probably would have laughed at you. 
Myself and 10 other developers in my company went through a day of BDD / TDD training, with Scott Bellware, yesterday. It was a lot of fun, very challenging at times, and covered a lot of topics including an overview of Agile and Behavior Driven Development, all the way down to writing Specification Tests, doing Test Driven Development and refactoring the model to improve readability, maintainability, flexibility, etc. I took notes via index cards (love that cool-aid) and wanted to share. I don't expect these notes to make sense to everyone. Hopefully it will spark some dialog in someone's mind and cause them to dig further. First off - the quote of the day. "Can I be honest with you and say that I've been wanting to touch your keyboard, all day?" Now for my notes.  User Stories [Role], [Goal], [Motiviation] - As a [role], I want to [goal], so that [motivation]
- Example: As a nurse, I want to record a patients vital signs, so that I can determine their medication and care needs
- Motivation is critical - it determines how the development team understands and implements the story. It determines the user experience, how things are integrated, how the software is designed, etc.
Acceptance Criteria - Acceptance Criteria is used to drive code, not the story, directly
- may change at any point, up to implementation
- is used to drive code design, test design, implementations, etc.
- should be spoken in domain language
- may include non-functional, technical details such as database tables, infrastructure, performance, etc
- All acceptance criteria must be met and tested / verified before a story is considered done
Specification Tests - Test Fixture per Class is an anti-pattern (on a personal note, this problem bothered me for months before I discovered BDD)
- Context Specification or Behavior Specification testing
- When [verb] then [verb]
- "When [verb]" is the context
- "Then [verb]" is an observation of the behavior
- Based on Acceptance Criteria, but not "code-gen'd" from acceptance criteria
Story Estimation - Agile Poker: uses generalized Fibonacci sequence as order of complexity
- "?", 0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100, infinite
- everyone throws their estimate at same time
- if estimates have significant outliers, discussion occurs to understand why, get more detail, etc. and re-throw may happen
Entity Data vs. Aggregate Data - Entities should never contain aggregate data
- Aggregate data is for reporting and other aggregate needs
- If you need aggregate data to process something, write an SQL query, stored proc, etc. - don't use an ORM like NHibernate
- We don't want a "Customer" entity to need 10,000 "Order" entities, to aggregate data for processing; write a query to aggregate instead
- We don't want to persist data that can be calculated / aggregated, generally (performance issues may override this)
Domain Services - Can have dependencies on external systems
- are part of domain logic, therefore are in domain model / assembly
- are "Doers" of process that don't fit into entity and entity logic, directly
- coordination of entity logic
- can include calls to data access, logging, etc.
Continuous Integration - Not just continuous compilation of code
- Full end to end integration of all code, components, databases, services, etc
- Full suite of integration testing including database testing
- Do not allow commits if build is currently broken
- do not allow defects to live - fix immediately, to fix build
- "Defect" is broken software, "Bug" is functional but wrong
Daily Scrum - 3 Questions everyone answers:
- What did I do yesterday?
- What am I doing today?
- What issues am I having?
- Each person should answer quickly - 1 or 2 minutes, max
- further discussion happens outside of the Scrum meeting
Productivity of Dev Team - RAD and other non-review, non-iterative based management causes problems and loss of productivity
- we need constant review of the design to ensure good design
- shorten the feedback loop and get constant review of the design, to always improve the design, via pair programming, work cells, retrospectives, etc.
- good design will cause productivity gains in the development team beyond the capabilities of any tools
Whiteboard Diagramming vs. Details Specs - White board diagramming and human interaction is always better than detailed documents and specs in UML
- Human interaction leads to knowledge crunching and learning, not just reading a repeating
- Take pictures, don't re-draw in UML; don't waste time with it
- Video the entire conversation is even better, so others can learn from the knowledge crunching that occurs; capture the human interaction, body language, etc.
I'm reading "Lean Software Development: An Agile Toolkit", and the first paragraph under "Tool 11: Queuing Theory" talks about the bottleneck that often occurs in the test lab - not enough testers, too much work for the number of testers, etc. "We have often heard the lament 'My biggest problem is the testing department.' Now, testing people are very nice people: dedicated, hard working, and very important to the development effort. But there never seems to be enough of them to go around. And although the developers might write their own unit tests, testers frequently do acceptance testing. So, without enough testers, the whole development process bogs down." The rest of the chapter talks about the queuing theory that can be applied to help alleviate the issue. It's a great chapter with lots of good information. I have a problem with the idea of applying this type of queuing theory to the the test lab, as a bottleneck, though. First, let me state some assumptions about the primary responsibility of the testers in the test lab: - Testers are writing automated acceptance tests, for automated regression testing and integration testing
- Testers are also doing human interaction testing, for the "human touch" of usability, etc.
If the testers are the bottleneck, and the two primary functions of the testers are as I have listed, then I think the there is a much more simple solution to the problem, from a lean perspective: let the developers write the automated acceptance tests. Assuming that the developers are already writing unit tests, and are therefore capable of writing code to test code, it makes a lot of sense in my mind that the developers should be writing the majority of the automated acceptance tests. It all goes back to the idea of flow - ensuring that the entire system (or process) has a smooth flow from beginning to end. This means that we may need to sub-optimize one area for the benefit of the whole, but the end result is that we will have a better system or process by making the entire flow as smooth as possible. Counteracting Mura - or "Let's make it smooooooth" If we are looking at the test lab as a bottleneck - a rough spot in the flow of our software development cycles - then let's take the most simple course of action possible, to reduce that rough spot as much as possible. Rather than spending so much time and effort on queuing theory and implementation, let's find a way to remove the bottleneck. Assume that the software developers are experts at writing code - and writing code to test their code. Doesn't it make sense, then, that the software developers should be writing the acceptance tests, even if the acceptance tests are being specified by the customer and test lab personnel? If we allow the developers to take a little more responsibility, we may be sub-optimizing the development department a little. But, by doing so we are freeing up the much more scarce resources of the test lab and we can then make adjustments to the test lab's queue and workload, if needed. The idea of leveling out the flow of the system like this can be traced back to the Japanese term, Mura. The Wikipedia entry says it all: "The fact that there is one operator will force a smoothness across the operations because the workpiece flows with the operator." In this case, we are calling the combination of production code, unit tests and acceptance tests, the "workpiece". I believe this is a fair assesment, since the code and tests are all going to be based on a feature, use case, or user story. In fact, I would say that the workpeice actually is the feature, use case or user story that is being worked on. The code, unit tests and acceptance tests could be considered the artifacts products by the workpeice flowing through the system. ... but that's just splitting hairs, really. It's all about Occam's razor, Parsimony, KISS, or whatever you want to call it - the simple solution is often the correct solution (simple, however, doesn't always mean easy). The Need for the Test Lab I'm certainly not saying we don't need a test lab. The testers are (supposed to be) experts in interaction testing, usability testing, and adding that "human touch". We absolutely need that perspective on those aspects of software testing that can't reasonably be automated. I am advocating that we find a better way to smooth the flow of the system - rather than apply complex theories and equations to the situation, find a solution that doesn't require anything complex. Conclusions In the end, the problem of the test lab bottleneck can be solved many different ways. You might level the system via Pair +1 Programming or some other form of involving the developers in writing the automated acceptance tests. Perhaps you make the testers part of the team and have them writing the automated tests at the same time as the developers writing code. You might still need to employ queuing theory. Either way, try to find the solution that works best to smooth out the process for your team.
Based on the proposed information and syntax from My Previous Post, I've created a basic Design By Contract unit testing framework. The intent of the code so far, is to provide a quick-and-dirty proof of concept. With that in mind, I give you DBCUnit hosted on GoogleCode You can get the source code via Subversion: http://dbcunit.googlecode.com/svn/trunk/ Please note that the existing code only supports one assertion so far: Equals. Also, the execution engine is entirely made up of terrible code that assumes a lot of perfect-scenario input. I'm planning to flesh it out more and make the code more sustainable - actually adding Contract specifications for my execution engine, etc. Let me know what you think of the idea and the syntax. Also feel free to join the project and pitch in for syntax and implementation. The one Contract I have specified so far, just to get rolling, is that a [PreCondition] should be executed once. [Condition] public class WhenPreConditionIsPresentInContractCondition { private int preConditionExecutionCount = 0; [PreCondition] public void PreCondition() { preConditionExecutionCount += 1; } [PostCondition] public void ThePreConditionIsExecutedOnlyOnce() { Assert.That(preConditionExecutionCount).Equals(1); } }
To run the test, run build the solution and run the DBCUnit.Console pointing to the DBCUnit.Contracts.dll, like this:
C:\...\> DBCUnit.Console.exe DBCUnit.Contracts.dll
If the test succeeds, there is currently no message printed to the console window. If it fails, it will write out a message saying what value it expected and what the value was. It's all very simplistic at this point, just a proof of concept. I also set up the DBCUnit.Console project to automatically start with the "DBCUnit.Contracts.dll" as the startup parameter, so you can step into the code via debugger and see it in action.
Have fun, and don't laugh too much. This is my first attempt at hacking together a unit testing framework.
There's a lot of talk about Design By Contract (DBC) out there in the development world. Various development languages have varying support for it, but more importantly various processes have various levels of support for it. It seems, though, that the farther down the path of development we travel, the more important it is for us to consider DBC in the code that we write. Large projects with multiple developers are in great need of DBC. Projects that have publicly distributed API's are in even greater need of DBC. Even if you are working on a simple, one person project for yourself, and you are the only one that will ever use it's methods and objects, I'm willing to bet that you will forget about the assumptions that you are making when writing the methods and objects, at some point. So where does this distinct need for DBC leave us, in the world of .NET (C#, VB, and the other "common" .NET languages)? We still need a way to enforce DBC, but our language of choice doesn't support it, natively. So we have two real choices (excluding DSL writing, and/or switching languages) - documentation (via code comments or written / published documentation) or Unit Tests. Yes, that's right - Unit Tests are not just for testing, anymore. Or more correctly, the tests executed by unit testing are not just for the sake of testing. The intention is verify the pre and post conditions of a design-by-contract. Of course, I'm not the first one to suggest this. It's mentioned briefly in "Agile Principles, Patterns, and Practices in C#" by Robert C. Martin and countless other times as well. I am propose a new unit testing framework. I know, I know... "not ANOTHER xUnit framework... *sigh* ". In this case, I am proposing a semantic change along with the mechanical (syntax) change, specifically for the purpose of introducing unit testing to a group of developers that may not believe in "Unit Testing". As an example of my proposed syntax, in a file called "SomeContract.cs", this test code would exist: [Conditional] public class WhenSomeConditionIsMet { SomeValue someValue; [PreCondition] public void PreCondition() { Setup.My.Inputs inputs = Here; } [Execution] public void Execution() { someValue = Execute.TheContract.With(inputs); } [PostCondition] public void ThenSomeOutputIsSomeValue() { Assert.That(someValue).Equals("Some Known Value"); } [PostCondition] public void ThenSomeOutputIsSomeValue() { Assert.That(someValue).DoesNotEqual("Some Unknown Value"); } }
To make this proposed syntax easier to understand, I'm basing it on the NUnit style of using attributes, at the moment; but it doesn't have to be that way. There is almost a one to one translation between NUnit and this.
- The [Conditional] attribute is equivalent to [TestFixture]
- The [PreCondition] attribute is equivalent to [TestFixtureSetup]
- The [Execution] attribute is equivalent to [Setup]
- The [PostCondition] attribute is equivalent to [Test]
In this simple example, I don't have an equivalent to [Teardown] or [TestFixtureTearDown]. I'm sure I'll need those at some point, but until I see the need, I'm not going to worry about them. I also don't really care about the Assert syntax. I'm just putting that syntax in place to illustrate the point.
Where I differ from typical NUnit style of testing is that I want to see a single "PreCondition" and "Execution" per test fixture, and have multiple "PostConditions" that only contain the assert statements. This style of test code more closely resembles that of Scott Bellware's SpecUnit.NET and for good reason - I like it. I'm a fan of having as little as possible in the method that does the assert - keep it simple and explicit.
The biggest problem I have with my proposed syntax is a problem inherent to Design By Contract - the idea that you know the object (contract) being executed. A huge part of why I love Behavior/Specification Testing vs. Unit Testing is that Unit Tests and TestFixtures typically tell you that for class/file "XYZ.cs" you have "TestFixtureXYZ.cs". Whereas, Behavior/Specification Testing says that we have "BehaviorSpecification.cs" regardless of the classes used to implement it. I love this about Behavior Driven Development - it freed my mind from the horrible constraints that I saw in standard Unit Testing / TestFixtures. Unfortunately, Design By Contract basically takes us right back to the same place. We are specifying contract (class) "XYZ" so we have a "XYZContract.cs" file to hold all of our [Conditional]s.
...
Does anyone else see any value in this style of unit testing? I can certainly see scenarios where this would appeal to some developers more than the standard xUnit frameworks.
A coworker and I often have conversations about Unit Testing vs. Test Driven Development. Generally speaking, we agree - there are some semantic or mechanical differences in what we're saying, but nothing major and we usually work that out through the conversations, defining what we are saying. Recently he asked if I ever allow myself to write any code without unit tests, or write code before unit testing it. My initial answer was no, not surprisingly. However, after discussing the question and it's implications further, he brought up a good point and a scenario where I highly encourage writing code without tests: Prototyping (or Spiking, in Agile terms). I've posted in the past about how I believe that Prototyping A Process is important in software development, so I won't completely re-hash that. Although, the language that I use to describe prototyping may be evolving, the core concepts and process are still in place (the spiking concept is the same as what I called Prototyping). Here's what ExtremeProgramming.com has to say about Spiking: "Create spike solutions to figure out answers to tough technical or design problems. A spike solution is a very simple program to explore potential solutions. Build a system which only addresses the problem under examination and ignore all other concerns. Most spikes are not good enough to keep, so expect to throw it away. The goal is reducing the risk of a technical problem or increase the reliability of a user story's estimate. When a technical difficulty threatens to hold up the system's development put a pair of developers on the problem for a week or two and reduce the potential risk. " This may seem counter to the creed of writing unit tests first and even counter to the creed of not coding for the future. There is a key element in this description, which I believe is not emphasized nearly enough. The code in your spike IS throw-away code. DO NOT copy and paste even one line of code from the spike into the production code. "Copy and paste is a design error." - David Parnas When you understand the process, technology or whatever it is that you are learning, well enough, you must step back from that solution and back into your actual project. Then, you continue the test-first process of Test Driven Development - you write your tests for the area that you are covering and then you write the implementation code using the spike as a read-only reference. So, yes - there is a time and place for writing code without any unit tests; production code is never that place, though.
A lot of people ask these questions when they first start unit testing - How many unit tests is too many?
- Do I need to cover every property, every individual method, ever object, every ???
The goal of unit testing is to provide 100% test coverage. The reality of unit testing is that you want 95% or more, test coverage. There are occasions when unit testing that one last line of code is horrendously repetitious or you miss something or accidentally couple something too tightly. But wait... there's more... and those seem like lousy excuses that lead to allowing bad design in your code. Ultra-Fine Granularity is Horrible If you are writing your unit tests after you write your production code, or if you are writing your unit tests first but are simply going through the mechanical process switch and it doesn't really matter if you write your tests first or not, then the answer is horrible. You'll end up unit testing way more than you need to. For example, I wrote a login screen last year. This login screen has three fields and two buttons on it: Username, Password, a drop list of locations assigned to the username, a Login button and a Cancel button. How many unit tests do you think should be written for this? ... I wrote 27 unit tests to cover every possible edge case in the presenter that controlled this view. What a giant horrible mess - changing anything in that login screen was almost as bad as not having it unit tested at all (well ok... nothing is that bad) I ended up unit testing setting an individual property, and then checking to make sure that property was stored correctly. I unit tested individual method calls with only the username set, or only the password set, or only the location set, or only whatever combination of those set. I unit tested loading the list of locations for the username, and ensuring that the location selected is valid for the user. I unit tested what would happen is an invalid location was selected or a null location was selected... every possible edge case was unit tested and it drove bad design into the application because no one wanted to go through the pain of having to change all of those unit tests at that level of granularity. Step Up To The API Just unit testing your code is a great way to ensure that you are writing way more unit tests than you need. Chances are, the code you are writing is not very cohesive and you will end up unit testing the read and write of individual properties rather than just unit testing the business value (process) that actually reads / writes the individual properties. That is to say, your unit tests should be written at one or two steps above ultra-fine granularity. Don't test the individual properties, test that API that you want to call, that has business value. So, how do you account for 100% code coverage if you are not unit testing the properties and all of the edge cases? Never write code that you don't need, right now. If you are writing a unit test and the test or the implementation needs a property, then you create that property for that unit test at that time. This does not mean that you write a bunch of get / set property unit tests, just so you can unit test the properties. This means that you specify the business value API in your unit test, and by virtue of having business value, you will likely have various properties associated with the classes in that API. The same is true for edge cases - if the business value of the unit test does not handle the edge cases, then there are no edge cases. Only when you have business value specifying an edge case, do you need to write a unit test for the edge case and possibly modify code to handle the edge case. Ok, then what happens if your code changes and you don't call that property in the original unit test, anymore? Never leave dead code in your system. Ever. Period. End of discussion. If you change your unit tests because the design of the object(s) change, and you are no longer using a property - delete the property! If you delete it and you find that you can't compile the code any longer because other parts of the system need that property, then you need to evaluate whether or not that property is really providing value to those other places vs. changing those other places to match the new design. Test First vs. Test After A big part of figuring out how many unit tests you need is understanding the functionality of the system. You should be writing a unit test for every functional point of the code, achieving 100% code coverage. The problem with the original question of how many unit tests to write, though, is that there is a hidden assumption in that question: "I wrote my code, now how many tests do I need, to cover it correctly?" This question is an underlying problem in Unit Testing and simple Test First development. If you are just unit testing your existing code or only going through the mechanical process switch of writing a unit test first, but not really using the test to drive your design, then you are likely not going to see some of the major benefits of Test Driven DESIGN / Development: not writing code you don't need, and creating the API that you want to call instead of the API coming together haphazardly as a bi-product of writing code first. When you take the step up to unit testing the API, it becomes more apparent that you really want to specify the API before you write it. If you specify the API before you write it, then you are one step closer to true Test Driven Development. Don't expect the test to design your code for you. Use the test to flesh out your design before you write any code. Test Driven DESIGN / Development Would you rather: Write 50+ lines of code into your model, then write a unit test that shows an ugly API causing you to go back to the code and re-write it in the hopes that it will produce a better API, most likely repeating this process once or twice until you get frustrated with changing your code because it takes so long or Write 5 lines of unit test code, specifying the API that you want, realizing that it's not going to work and changing 2 or lines of that test, going through this cycle 5 or 6 times until you have the API that you really do want to call; then implementing the API in the 50+ lines of code and being done with it I'll take #2. I don't like rewriting large chunks of code. Rewriting 2 or 3 lines of code is easy - I'll do that any minute of any day. Chances are, if you are willing to write the correct number of unit tests by specifying the higher level API in your unit tests, you will gravitate toward designing your API in your unit tests. TDD Misconception: TDD is NOT a design tool. It is not "the answer". Is will not design your application for you. It will not solve your problems for you. If you don't know how to design software, then you need to get some training on design patterns, loose coupling through single responsibility and separation of concerns, and various other core foundations of good Object Oriented Development. In reality, Test Driven Development is just an easier way of saying this: "Design your API in the context of a unit test, so that you have your API implementation covered by unit tests before you even write the implementation." Conclusions: In the end, we can answer the original questions from this post by re-stating Test Driven Development as a software development guideline: "Design via code, unit testing 100% as you go."
|