In addition to our 'Code Review Challenge' that I discussed recently, we have been using another retrospective game - 'Name That Standard'. The idea is fairly simple. At the beginning of any retrospective, we go around the room and ask each team member to list out a standard that we are using in our project. This can be any standard that anyone feels the team is using or needs to be using, including coding conventions, architectural patterns, business and project management processes, communication means, etc. The intention is similar to the intent of 'The Code Review Challenge' - socialization of the the system. Only in this case, we are applying the term 'the system' at a much higher level - our organizational and project management / implementation system as a whole. Some thoughts on running 'Name That Standard': - Require not only naming of the standard, but accurate description of the standard and an example of where it is used. Alternatively, you may want to list a brief description out on a projector or whiteboard and see who can name the standard being described.
- Encourage the team to think about the team as a whole, not just their area of responsibility.
- Encourage the team to list standards that they don't understand, so that the rest of the team can chime in with assistance and help the team member in question grow
- Don't just let people yell out standards. go around the room, one person at a time. we got a little chaotic at first, and it was hard to know who said what.
- Encourage people to ask questions like 'i thought we were using this standard, but i saw someone else doing that standard'
- Encourage the standards to become more and more low level and detailed, over time (over multiple iterations)
- Encourage the standards to evolve and change for the better, over time (over multiple iterations)
In our implementations of this game, we've managed to get a fairly good list of standards written down and also resolved some questions on what standards we actually are using. For example, we had 3 dynamic mocking frameworks in our code base - Rhino Mocks, Moq, and a home grown one that we lovingly called 'BrandoMocks' (named for our team member, Brandon, who wrote it for fun one night). The end result of that discussion was to throw out Moq and BrandoMocks, adopting Rhino Mocks as our standard for this project. Having done 'Name That Standard' in two consecutive iteration retrospectives, I think I can say that it has been very successful. The team as a whole is becoming more and more aware of the standards that we are using, and the individual members are beginning to contribute more and more to the standards. With each passing iteration, and each new technique for creating collective ownership and socializing the system, I see the team coming together and really forming a team vs. a bunch of individual developers working on the same system.
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.
Here's the format that my team is currently using for User Stories and Acceptance Criteria. These formats are primarily learned from Scott Bellware's training that we have had recently, but is also influenced by the actual project that we are currently working. User Stories As a [Role], I want to [Goal], so that [Motivation]. Role: Who is using the system Goal: What you want to do with the system Motivation: Why you want to use the system. I typically see people asking questions about Motivation - why it's important, etc. It's important because it gives the implementer a frame of mind for the behaviour. Example: As a Nurse, I want to record patient pain levels, so that I can adjust their medication doses appropriately. vs. As a Nurse, I want to record patient pain levels, so that I can show the pain trends with certain types of medication over a period of time. When I read these two stories, as a developer, I begin to see drastic requirements differences and expect certain Acceptance Criteria based on the requirements differences that I expect to see. If I'm only adjusting their medication level, I probably only want to see what their current pain level is and whether or not I can adjust the level within dosage limitations. If I'm monitoring trends over a long period of time, I will want to record more - dose for period of time, when adjusted and why for the pain level, what other medications were involved in that patient in the same time periods, etc. The motivation can cause a huge difference in how the story is implemented. Acceptance Criteria I've noticed that there are two types of Acceptance Criteria: Behavioral (functional) and technical (non-functional). As such, there may be two different formats for a story. Functional Acceptance Criteria When [verb], should [verb], should ... Example: When recording the patient pain level Should represent the level on a scale of 1 to 10 where 1 is no pain and 10 is extreme pain Non-Functional Acceptance Criteria Should [technical detail] or When [functional criteria], Should [technical detail] Example: The pain level recording screen must be usable via a touch screen tablet pc. or When recording patient levels on a tablet pc, the data should be cached locally and synchronized to the master database when the nurse docs the tablet pc. I believe the functional acceptance criteria should be stated in technology and implementation agnostics terms - imagine for a moment that you are going to implement the requirements via building blocks, paper forms, or printed circuit logic. If you have to change the functional acceptance criteria based on the implementation technology, then the criteria is not formatted correctly in the first place. Avoid joining functional and non-functional acceptance criteria into a single acceptance criteria. It is easy for a developer to lose sight of the behavioral goals when these are mixed, and may prevent a UI/UX specialist from creating the greatest workflow ever.
There are a few examples in The Toyota Way, of American manufacturing companies that were thought to be world class Lean Manufacturers. When representatives from Toyota began working with these companies, though, the result was quite a shake-up. The companies that were thought to be lean, were merely implementing some of the surface level processes of lean manufacturing. They had not realized that there was a root cause of the process that they were implementing. The Principles, Not The Process A coworker recently asked me to help out with a presentation on Model-View-Presenter. He had some specific questions about why we would want to use MVP vs. MVC vs. any other UI pattern. Many of the questions centered around various benefits that have been promoted by myself and others out in the developer world via blogs and articles - items such as testability, changeability, flexibility, and other "ilities". My answers to some of these questions surprised me. Model-View-Presenter is often thought of as a journey and an end in itself. When I first started learning MVP just over a year ago, this is exactly how I saw it. I started with the intention of learning how to separate my core process from the form implementation, with the intention of being able to unit test the core process. I was working under the impression that I could find a way to implement MVP that would truly allow me to swap out my UI - to the point of changing the workflow within the UI. I had the goal in the mind, of being able to write one presenter and have it support WinForms, WebForms, Web Services, and any other client-facing API. One year and several projects later, I have some good lessons learned. I have implemented various forms of MVP - automagic view injection, manual everything, and a lot of ideas in between. My understanding of MVP is fairly solid, and I am capable of implementing it in WinForms, WebForms, Web Services, and other client-facing API's. What I have realized recently, is that MVP is not (or, should not be) a journey and a goal in itself. MVP, MVC, and other UI related patterns are actually an effect of other underlying principles. The underlying principles of object oriented development - such as Separation of Concerns, Single Responsibility Principle, Dependency Inversion and others - are the real cause of MVP, MVC, etc. Testability, flexibility, and the other "ilities" are merely side-effects of good design and implementation. And while Testability is a valid goal in itself, seeking it as the primary or only goal will only lead you part way down the path. You will end up like the American manufacturers that thought they were world class lean companies, only to find out that they had barely scratched the surface of lean process. The Journey, Not The Goal Despite the success of Toyota as the creators of lean manufacturing, they believe that they are continuously learning about lean, so they can continuously improve. Similarly, despite my many years of software development experience, I now believe that I know little about software development. I have a good knowledge of .NET and general knowledge of Object Oriented Development, but I don't know much about other paradigms like Functional Languages or Duck-Typed languages such as Ruby. In my professional growth as a developer, I have set goals for myself at various times. Most of these goals were set with the belief that they would make me a great developer. However, I've found that every time I have reached a goal, there is an entirely new world of possibilities ahead and what I thought was a great developer was merely the beginning of a journey. I've discovered that the journey itself is often far more important than what I believed the goal to be. I'm not advocating that you forget about setting goals and let your career happen as it will - that's a great way to become burned-out, outdated, and irrelevant. What I am advocating is that when we have a goal in mind, we set out with the understanding that the goal itself may only be a small leg on a long journey. In this case, the journey is the process of learning. The journey itself should be viewed as the long term strategy, to the point where you allow the individual goals to come and go as needed. You may not accomplish a specific set goal - but the experience gained while working toward that goal may open up opportunity for other goals to be reached easily. Learn The Principles Via The Process All this talk about knowing the underlying principles is great - but how can we understand the principles and philosophies without knowing the process? Unfortunately, I don't know if that's possible for most of us. I'm sure that somewhere out there, someone has read SRP, DI, SoC, and other principles, and as a result began writing code that was modeled in a manner similar to MVP (Martin Fowler, for example). For the majority of us, though, learning the principles is done by first learning the process - study the implementation of the effect to learn the cause. Conclusions The journey itself is how we build our experience and understanding. For most of us, that journey begins with a goal of learning a new process. Find a goal or two that you believe will help you be a better developer and learn the processes. Along the journey of reaching for this goal, always keep in mind that there is an underlying principle or philosophy that enables the process. By learning the principles and philosophies behind the process, the process itself becomes a trivial matter - an implementation of the principles and philosophies, and not a goal in itself.
The Toyota Way mentions the concept of Jidoka in chapter 1 (and probably other places that I haven't read yet). On page 6, in the "4 P" diagram, jidoka is described as "Stop when there is a quality problem" Wikipedia calls the same concept "Autonomation" and says it may be described as "intelligent automation" or "automation with a human touch" and "At Toyota this usually means that if an abnormal situation arises the machine stops and the worker will stop the production line." We can apply the same principles in software development in many different ways. One of the more common implementations is the use of Continuous Integration and Automated Testing. According to the notes I took during A Day Of Bellware, if our CI server says the build is broken, we need to immediately stop working and fix the problem. I've heard this before, and will likely hear it again. However, I never really understood why people would say this. After all, as long as the problem is fixed eventually, isn't that ok? In the training that day, Scott provided a excellent visualization of why we should fix it immediately. Waste and Rework To steal Scott's illustrations, consider the following image to be an example of "perfect" software. All of the edges of each module (block) are well defined and it's easy stack new blocks next to existing blocks. Figure 1. "Perfect" software
Now let's assume that somehwere in the coding process, someone accidentally causes a defect in the software. That defect could be represented by a buldge in one of the lines - as if the module was doing more than it should. Figure 2. A defect
The individual block that has the defect may not be that bad, at first glance - or when examined on it's own. And on it's own, the defect could be fixed. Jidoka would say that you need to stop immediately and fix the problem. So, what happens when you don't fix the problem right and and you try to stack another block to the right of the defect? Suddenly you find yourself re-shaping the next block in order to account for the problems in the previous one. Eventually, you may be able to smooth out the issues. Depending on the size of the issue, though, it may take several new modules to completely normalize things. Figure 3. The effect of a defect
Now, 3 day, 3 weeks, 3 months, or whatever period of time later, you have a significantly larger problem to deal with. Let's go back and fix the original cause of the problem, to start with. What happens to the rest of the code that was warped around the original defect? The warped code in the rest of the system also has to be fixed. Simply removing the original bulge does not mean that the rest of the warps will magically disappear as well. Figure 4. Removing the original bulge
How much re-work will it take to fix the rest of the system that was warped around the original problem? How much time and effort will be wasted because the original defect was not addressed immediately? These questions can only be answered in the context of your problems. If this defect is one line of code in one module, maybe it's not so bad. But if this defect is an entire module used by many other modules, the time and cost could be huge. Conclusion: Fix It Now If the continuous integration server says that our build is broken; or if the customer says "the software does not break, but piece XYZ isn't correct"; or ... pick a problem in your system; we need to respond as quickly as possible, in order to prevent the rest of the system from being warped by the original problem. Under the assumption that we want to fix a defect as soon as possible - how do we ensure that we know about the problem as soon as possible? Whatever your implementation of this solution is, the solution to this problem comes down to shortening the feedback loop. If you are notified of the problem in 20 minutes vs. 20 days, there will be significantly less damage to the overall system and significantly less re-work and waste caused by the defect.
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.
The topic of Model-View-Presenter starter resources came up at work, today, and I like the list I pulled together. So, I thought I would share with the rest of the world. This is by no means a comprehensive list, or even "the best" list - it's only what I pulled together in 10 minutes. The original source of MVP : http://www.martinfowler.com/eaaDev/ModelViewPresenter.html My own best practices for MVP, following the Passive View mentality http://www.avocadosoftware.com/csblogs/dredge/archive/2008/02/20/787.aspx This article should solidify a lot of what I am talking about in my best practices http://msdn.microsoft.com/en-us/magazine/cc188690.aspx Jeremy Miller was one of the guys to originally start pushing MVP into the ASP.NET community. He’s got a lot of good articles on his blog http://codebetter.com/blogs/jeremy.miller/archive/2006/02/16/138382.aspx This was the article that really solidified my understanding of the basics of MVP, back when I first started learning it http://www.codeproject.com/KB/architecture/ModelViewPresenter.aspx Aside from those core MVP articles, there are several key concepts that you’ll want to understand in order to really be able to present / teach MVP, including: - Single Responsibility Principle
- Open/Closed Principle
- Dependency Inversion/Injection and Inversion of Control
- Liskov Substitution Principle
The key to these principles is really the subtlety that underlines them. For example, the Liskov Substitution Principle isn’t just a fancy name for polymorphism – it’s what makes polymorphism work. You certainly are not required to know these principles forward and backward to be able to implement MVP, but you’ll find that the question of “why?” is easier to answer to if you do know them. Honestly, when it comes down to it, these principles are what make object oriented development work – they are how you achieve the high cohesion, low coupling, and tight encapsulation that OOD wants. There are plenty of other principles (such as Orthogonality, the law of Demeter, and others) that will help you achieve this as well; but this is a starter-list, not the all encompassing list. One of the books I've been reading recently, that covers these topics and more, is "Agile Principles, Patterns, and Practices in C#" by Robert Martin and Martin Micah.  I highly recommend this book, not just for these topics. It's a great book, all around.
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.
I've been learning a lot lately - reading up on Agile, Lean Manufacturing, Lean Software Development; experimenting with NHibernate, exploring UnitOfWork concepts and generally trying to become a better software developer. In this process of learning, discussion with others, and application of knowledge to my environment, I've found that knowledge is very distinctly different than understanding. Let's look at my use of NHibernate as an example. 2 years ago, I knew the basics of what NHibernate could do for me - I spent a few weeks learning the very basics to see how it worked and whether or not I wanted to use it. 1 year ago, I actually started using NHibernate for a project and my knowledge of it quickly grew. I knew how to create the appropriate mapping files, configure NHibernate, etc. As this knowledge grew, I became more and more interested in what NHibernate could do and how it could be applied to many different applications and situations. I knew a lot about how NHibernate was built, how it worked, and what it's capabilities were. However, all of this knowledge was not a substitute for a real understanding of NHibernate. My knowledge of how it worked led me to some conclusions that I don't think are correct anymore - trying to apply NHibernate in situations where it really was not the right answer. I've seen the same problem occur multiple times in the last 6 months, and in the last 11+ years of my career. There's almost a recognizable pattern to the learning process: - Learning curve to gain working knowledge
- Knowledge growth and intimate familiarity
- Assumption that knowledge gained is a substitute for experience and understanding
- Attempts to apply knowledge incorrectly / in wrong situation
- Realization that knowledge is not understanding or experience
- Stepping back from knowledge to gain understanding of how/when to apply the knowledge
I've done this with Agile/Lean software development as well, recently. I've gained a significant academic knowledge of agile and it's processes and practices. Some of my knowledge is directly backed up by experience, so I believe I do have some understanding of the agile engineering practices. However, I've let my limited understanding mix itself into my academic knowledge a little too much. I've found myself in situations recently where I'm arguing a logical conclusion to a situation and applying it to another situation incorrectly. One of my coworkers likes to apply this adage to situations like this: If a hammer is the only tool you have, everything looks like a nail. I need to remember to step back from my raw knowledge - be it academic or real world use - and let common sense and experience interweave into understanding or the realization that I don't understand. I need to understand that just because I am currently holding a hammer, and thinking about hammers, that doesn't mean that the problem in front of me is a nail that needs to be hammered. The realization that knowledge is not a substitute for understanding can be a very distressing and/or humbling experience. The reality of learning, knowledge and understanding, though, is that I can have every last bit of knowledge on a subject - but without experience to guide me, I can't always understand the where/when/why/how of applying that knowledge appropriately.
I'm chugging the cool-aid as quickly as I can mix it. This morning, I opened a pack of index cards with the intention of taking notes in a non-linear fashion (oh, the freedom my brain finally found). By the end of the day, I was hanging user stories on my cubicle wall to manage all of my administrative responsibilities. After all - if it's good for managing software development, why wouldn't it work for managing my daily / weekly administrative tasks (server maintenance, training coordination, permissions on various things, working with various people for various goals, etc). On the left side of the divide (created by the wall segments - makes it easy) is my back-log. On the right is my in-progress list sorted by highest priority at the bottom. The done list is the trash can you can't see on the floor. 
One of the primary goals of Lean Manufacturing in the elimination of waste. This may seem like an obvious goal - after all, who wants waste in their system? What's really interesting, though, is seeing where the term "waste" is actually applied and how Toyota redefined what waste really is. Muda, the concept of the seven wastes in the Toyota Production System, is a very different concept of waste than most of us would consider. It talks about not only physical waste (trash), but also logical, time, and effort waste. These seven wastes include (from Wikipedia): - Defects: Quality defects prevent the customers from accepting the product produced.
- Overproduction: Overproduction is the production or acquisition of items before they are actually required.
- Transportation: Each time a product is moved it stands the risk of being damaged, lost, delayed, etc. as well as being a cost for no added value.
- Waiting: Refers to both the time spent by the workers waiting for resources to arrive, the queue for their products to empty as well as the capital sunk in goods and services that are not yet delivered to the customer.
- Inventory: Inventory; be it in the form of Raw Materials, Work-In-Progress (WIP), or Finished Goods, represents a capital outlay that has not yet produced an income either by the producer or for the consumer.
- Motion: As compared to Transportation, Motion refers to the producer or worker or equipment.
- Overprocessing: Using a more expensive or otherwise valuable resource than is needed for the task or adding features that are designed in but unneeded by the customer.
In "Lean Software Development: An Agile Toolkit", Tom and Mary discuss The Seven Wastes and offer a great set |