Agile and the .NET Community
Why does it seem that the .NET Community isn’t very active in the Agile community? I know that there are Agile practitioners in various .NET shops however, it seems that the overall representation is very minimal. Is it because of a lack of understanding of the Agile methodologies? Or is there a lack of support and resources for the .NET community when it comes to Agile practices?
I just got back from the Agile Development Practices by SQE and I felt like I was in the minority there. As I went from sessions on TDD to Design Patterns to Refactoring each time it was asked what platform each of the developers were on I was one of 2 or 3 that mentioned .NET. The majority was Java with a small representation of Ruby developers, which on one hand I’m glad because I got a great exposure to Java and Ruby, however on the other hand it raises the questions as to why.
So what is Agile?
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a planThat is, while there is value in the items on
the right, we value the items on the left more.
Looking at the principles behind the manifesto we see more of what really is the driving force of the Agile movement and begin to understand why it is important to all developers to grasp this concept.
- Our highest priority is to satisfy the customer
through early and continuous delivery
of valuable software.- Welcome changing requirements, even late in
development. Agile processes harness change for
the customer’s competitive advantage.- Deliver working software frequently, from a
couple of weeks to a couple of months, with a
preference to the shorter timescale.- Business people and developers must work
together daily throughout the project.- Build projects around motivated individuals.
Give them the environment and support they need,
and trust them to get the job done.- The most efficient and effective method of
conveying information to and within a development
team is face-to-face conversation.- Working software is the primary measure of progress.
- Agile processes promote sustainable development.
The sponsors, developers, and users should be able
to maintain a constant pace indefinitely.- Continuous attention to technical excellence
and good design enhances agility.- Simplicity–the art of maximizing the amount
of work not done–is essential.- The best architectures, requirements, and designs
emerge from self-organizing teams.- At regular intervals, the team reflects on how
to become more effective, then tunes and adjusts
its behavior accordingly.
The early continuous delivery of quality working software through continuous attention to simplicity, technical excellence, good design where the business and developers form a team to work together frequently reflecting how to become more effective through self organization.
This is just the beginning however, Agile goes beyond just what is stated in the manifesto. There are methodologies for all aspects of the business, from the executives with LEAN, to the team with SCRUM down to the individual developers with XP (Extreme Programming). Some of the concepts with in XP will be quite familiar to the .NET community, such as Test Driven Development (TDD) and Pair Programming.
I want to encourage all developers to really look into what Agile has to offer and then take it to your business and challenge them to implement Agile as your new methodology. If you are already doing Agile then start speaking up about it. Make your presence known and help evangelize Agile in the .NET community.
David Yancey
Getting to Testable Code
While TDD (Test Driven Development) isn’t a new concept for developers, for many it is still a foreign concept. It’s a complete paradigm shift, to change your thinking of writing your test first then the code. What purpose does it serve to write a test you know isn’t going to pass, only to later write the code to make the test pass?
Today we are going to look at a different approach to TDD and how we can utilize this approach to guide us to higher quality, testable code. You will also notice that we will end up with tests that can be included in our unit test suite.
Before we begin however I want to go over a few myths usually associated with TDD.
1. When doing TDD no code should be written that doesn’t have a test associated.
In a perfect world this would be ideal, however we don’t live in a perfect world and this is really just unreasonable. In actuality, what we want to achieve is all code that fulfills a business rule has an associated test. A business rule is defined as a behavior that meets a specific business requirement. For example: Interest calculation based upon the terms of a loan would be a business requirement, where as the color of a button would not.
2. TDD is the same as Unit Test development.
TDD isn’t about testing your code, it is about using test to confirm your functionality and design, where as Unit Test is about the continued confirmation of existing behaviors. Now that having been said, while TDD isn’t Unit Testing, the tests created during your TDD sessions should morph into behavioral unit tests.
3. Code Coverage needs to be 95% or better.
If you don’t have 100% code coverage from your TDD code, then you need to re-evaluate your approach. Where 95% code coverage becomes difficult is when you are building unit tests for your legacy code. In your unit test development, set your goal to have 90%+ behavioral code coverage. We will go over some tips on how to approach this later on.
Red | Green | Re-Factor
Red | Green | Re-Factor has been the standard approach to TDD since its inception with good reason. It teaches you to code only what you need to make the test pass, and then re-factor the code so that it is clean code, is extensible, and doesn’t violate any of the S.O.L.I.D principles. Do this enough and you will be able to visualize your code meeting these requirements before you write your first test, which will lead to TWD (Test While Developing). However; be careful, and don’t allow yourself to get “lazy” and not write any tests.
YAGNI
You Ain’t Going to Need It. This is the concept of developing only what you need, when you need it. While this is an important principle, and TDD helps meet this principle, it is important to make sure we don’t develop that will force us into violating the Open/Closed principle.
DRY
Don’t Repeat Yourself. While this concept is self-explanatory, it’s worth mentioning here. Alan Shalloway created his own law which goes like :
“When N things need to change and N>1, Shalloway will find at most N-1 of these things.”
Encapsulation helps avoid violating this law.
Business / Behavioral Requirements: Before you even write your first test, make sure you have your user story complete with any business agreements. You will also need any User Acceptance Tests (UAT’s) which will help define when the behavior is considered done. Even if this is a spike session, you need these items to understand what you are going to develop.
The Approach
Business Case
To better understand how this approach works, we are going to work through typical business case for a financial corporation. Our financial corporation needs a to calculate a payment schedule for a car loan. Given that we have the loan amount, interest rate, and term of loan, then we should be able to calculate the amount of payment for the length of the loan. Note: we now have a user story and user acceptance test.
Functional Test
The formula to determine the payment amount is highly available all over the internet. In our first test we will be testing our code version of the formula to confirm we have translated the formula over to code correctly. As you work through your tests remember to keep in mind the core concepts (Red|Green|Re-Factor, Yagni, DRY).
[Test] public void ShouldCalculatePayment() { double actualPayment; double expectedPayment = 400.76; double interestRate = (7.5/12)/100; //7.5% interest / month int loanTerm = 5*12; //60 Months of payments int loanAmount = 20000; //Formula //A=P*(r*Math.pow(1+r,N))/(Math.pow(1+r,N)-1) actualPayment = Math.Round((loanAmount*(interestRate * (Math.Pow(1 + interestRate, loanTerm)) / Math.Pow(1 + interestRate, loanTerm) - 1))), 2); Assert.AreEqual(expectedPayment, actualPayment); }
Encapsulate
Next step is to encapsulate our function into a method of its own. Run your test to make sure that it still passes.
[Test] public void ShouldCalculatePayment() { double actualPayment; double expectedPayment = 400.76; actualPayment = CalculatePayment(); Assert.AreEqual(expectedPayment, actualPayment); } public double CalculatePayment() { double interestRate = (7.5 / 12) / 100; //7.5% interest / month int loanTerm = 60; int loanAmount = 20000; //Formula //A=P*(r*Math.pow(1+r,N))/(Math.pow(1+r,N)-1) return Math.Round((loanAmount * (interestRate * (Math.Pow(1 + interestRate, loanTerm)) / (Math.Pow(1 + interestRate, loanTerm) - 1))), 2); }
Re-Factor
Looking at our CalculatePayment() method we can see that we have some re-factor points. First of all we have our interestRate, loanTerm, loanAmount hard coded. We will most likely want to make those variables method level parameters. Any other re-factoring will happen here that is related to our new method.
[Test] public void ShouldCalculatePayment() { double actualPayment; double expectedPayment = 400.76; double interestRate = (7.5 / 12) / 100; //7.5% interest / month int loanTerm = 60; int loanAmount = 20000; actualPayment = CalculatePayment(interestRate, loanTerm, loanAmount); Assert.AreEqual(expectedPayment, actualPayment); } public double CalculatePayment(double interestRate, int loanTerm, int loanAmount) { //Formula //A=P*(r*Math.pow(1+r,N))/(Math.pow(1+r,N)-1) return Math.Round((loanAmount * (interestRate * (Math.Pow(1 + interestRate, loanTerm)) / (Math.Pow(1 + interestRate, loanTerm) - 1))), 2); }
Design
Final step is the design step. This is where we would take our method to an existing class or a new class depending upon what the business requirements are. I will leave the final step for you to complete in this exercise. Once you have completed the final step you will have a new unit test for your unit test suite which meets a specific business behavior/requirement.
Note: If your final test includes interaction with the database/filesystem then you may want to look at re-factoring your test so that you are mocking the database/filesystem instead of directly interacting with them in the test. The reason for this is that data/files change, which would then cause tests to fail. These failures would be considered ‘false failures’.
Conclusion
When I talk to developers about TDD/Unit Testing, I generally get one of two reasons that they don’t do either. One is that they don’t know how to approach TDD/Unit testing, and the other is that it takes too long to do TDD/Unit Testing. I will answer the later first. As developers you will test. If you don’t do any type of TDD/Unit testing, then you will be testing the code when it comes back to you from QA. But rest assured you will be testing. You will gain greater confidence in yourself and with your team if you can deliver quality/provable/testable code. TDD/Unit Testing is a tool that enables you to do just that. As for the first reason developers don’t do TDD, if you still don’t know how to approach it, re-read this article.
Happy Coding
Dave