This article deals with an idea that I believe to be one of the key concepts of modern programming. When I say modern, I'm referencing to methodologies that re-appeared with hype names and excessive power during the second half of the 90's and the beginning of the 21st century as an answer to the bureaucratic, slow and heavy regulated methods in use at the time. As some of their examples are: ASD(Adaptive Software Development), DSDM (Dynamic Systems Development Method), Scrum, XP(Extreme Programming) among others...
These methodologies, that were known as lightweight methods until year 2001 and then became the Agile Software Development, have some aspects in common. At first, they were "born" from the developers necessity to focus more on the product in which they're working on than in hard processes related to development. It means that, unlike the classic long-term strategies and specifications that predict and document the entire project from the beginning to the end, the development is made in small iterations, with minimal planning. Another important factor is that these methodologies are in favour of face-to-face communication rather than written documentation among the team members (which are small, around 5-9 people). Third and most important to all programmers, in my humble opinion, is the constant execution of automated tests in all steps to ensure the quality of each small unit of code they generate.
Despite being an interesting topic, I'll stop my wondering here, because it's not my goal to teach techniques to manage software teams, but to teach one of the things that all of those techniques will be require you to do. Before anything, I want to remind you that a programmer will always program, so that doesn't matter too much what methodology is adopted by the company where you are working on. For fun, below is a strip from Geek Hero Comic that was kindly provided by the author Salvatore Iovene for this article:
For those who don't know yet, this is the third article of a series that I committed myself to write. Here is the list of articles:
- ASP, a misinterpreted technology
- Event-Driven-Programming and lambda function in ASP/VBScript.
- TDD (Test Driven Development) in ASP/VBScript.
- Languages: Based on Objects and Object Oriented.
- Object Oriented in ASP/VBScript "Hackers way".
- "Scripting Components", the ace in the role.
- Caching: the concept of DRY (Don't Repeat Yourself) applied to ASP.
If you're reading one of my articles for the first time, I strongly recommend that you read the previous ones first, because I'm trying to lead you into a great abstraction, presenting the topics incrementally.
Test Driven Development
First of all, TDD is not just a simple method to test the code, but rather a philosophy to develop it. Why is it important? Well, to get an idea, like this NIST (National Institute of Standards and Technology) points, an estimated 59.5 billion dollars or 0.6% of the gross domestic product are lost due to software bugs. The article says many interesting things, for example: "more than a third of these costs, or an estimated $22.2 billion, could be eliminated by an improved testing infrastructure that enables earlier and more effective identification and removal of software defects", "Currently, over half of all errors are not found until "downstream" in the development process or during post-sale software use.", but wait, don't be horrified, not everything is programmers fault: "Other factors contributing to quality problems include marketing strategies, limited liability by software vendors, and decreasing returns on testing and debugging", "The increasing complexity of software, along with a decreasing average product life expectancy, has increased the economic costs of errors" and so on... - If you have time, I suggest reading the entire article.
Ok, given the motivation (it's always good to be motivated to learn something new), let's go to the algorithm of the methodology (taken from the book "Test Driven Development: By Example" - Kent Beck) with some additional comments:
Add a test
In TDD, each new feature begins with the writing of a test. To write a test, the developer must clearly understand the specifications and requirements of the features. It can be achieved through use cases and user reports that covers the requirements and exceptions. This can also generate variations or modifications of existing tests. Anyway, this is a differential of TDD - the developer should focus on the requirements before starting to code, a subtle but important difference.
Run all tests and fail
This step validates that the test was well consolidated and the new test doesn't pass the old code without any changes. It's important that the written test fails. Otherwise, the test doesn't have any value, or the feature is already there.
Make a change
Here, you must code an algorithm that passes the test. The code written in this step won't be perfect and may, for example, pass the test in a tacky way. This is still accepted in this step, since the subsequent process steps will have to improve and refine the code.
Run all tests and pass
If all tests pass now, the programmer can be sure that the code meets all the requirements. It's a good place to begin the last step of the algorithm.
Rewriting the code
Now the code can be improved as necessary. Re-running all tests, the programmer is sure that the rewriting is not affecting any existing functionality. The concept of removing duplication is important in any software design.
That's it, now you know all the steps of this methodology. To implement a new functionality, always begin with the step number 1. It's highly recommended that the amount of changes between the tests never be large, so the programmer can simply use the undo instead of debug the program to find the error.
Using the knowledge
Suppose that you need to implement a new method in a program. The method is called factorial and receives a natural number as argument. It's function is to be equivalent to the n! operation of mathematics, except it should return (-1) when unable to perform the task. Learn more about the factorial operation.
So let's begin creating our test:
dim Tester : set Tester = new UnitTest
Response.write "Testing" & vbNewline
Response.write "=======" & vbNewline
Response.write Tester.test("factorial", array(-1), -1) & vbNewline
Response.write Tester.test("factorial", array(0), 1) & vbNewline
Response.write Tester.test("factorial", array(1), 1) & vbNewline
Response.write Tester.test("factorial", array(2), 2) & vbNewline
Response.write Tester.test("factorial", array(3), 6) & vbNewline
Response.write Tester.test("factorial", array("string"), -1) & vbNewline
Response.write Tester.test("factorial", array(2.718281828), -1) & vbNewline
set Tester = nothing
This test should return:
Failure: factorial(-1) != (integer)-1. Method returns: (empty)
Failure: factorial(0) != (integer)1. Method returns: (empty)
Failure: factorial(1) != (integer)1. Method returns: (empty)
Failure: factorial(2) != (integer)2. Method returns: (empty)
Failure: factorial(3) != (integer)6. Method returns: (empty)
Failure: factorial("string") != (integer)-1. Method returns: (empty)
Failure: factorial(2.718281828) != (integer)-1. Method returns: (empty)
Which was exactly what we were expecting. All tests failed because this function doesn't exist yet! As everything went wrong, the step 2 was a success, let's keep going to step 3 and include the following function:
if( lcase(typename(n)) = "integer" ) then
if( n >= 0 ) then
factorial = 1
dim i : for i = n to 2 step (-1)
factorial = factorial * i
factorial = (-1)
factorial = (-1)
Run the test again and receive:
Success: factorial(-1) == (integer)-1
Success: factorial(0) == (integer)1
Success: factorial(1) == (integer)1
Success: factorial(2) == (integer)2
Success: factorial(3) == (integer)6
Success: factorial("string") == (integer)-1
Success: factorial(2.718281828) == (integer)-1
Yes! The function, at least, works. Now it's up to you to improve it and re-test to ensure it still works as desired. Take a look at the final version:
if(vartype(n) <> 2) then factorial = (-1) : exit function
if(n < 0) then factorial = (-1) : exit function
if(n = 0) then factorial = 1 : exit function
factorial = n * factorial(n - 1)
The above code shows a very particular and interesting feature of VBScript. It can't be written as:
As we're used to in other languages, because VBScript doesn't work with short-circuiting logical operations (despite VB has
andalso) that are instructions
where the interpreter can skip the analysis of the second expression depending on the outcome of the first one. This means that if we put both conditions together in the same
the compiler analyses it as a single expression and immediately prints the error
type mismatch for non-integer. Therefore, when asked if the data is
on error resume next,
it answer yes, even when the content is
As I used in the examples some of the AXE(ASP Xtreme Evolution) includes, I prepared this zip file containing the final test and the files needed to run it. By the way, this is one of the major features of the AXE Framework: many of it's components were built in a way that doesn't require the complete platform to work. So, if you need any component that the AXE offers, for example: upload, just copy the files, their dependencies, and you will be fine. Everything runs perfectly!
Like the Toyotism in the automobile production history, which preached among other things:
- Flexible mechanization as opposed to the rigid Fordist automation,
- Total quality, which implements quality controls for all employees in all parts of the production process as opposed to the quality assured by sampling,
- Customized products
- Just-in-time, which aims to "produce the required, in the necessary amount and only when necessary"
and revolutionized the process of building, I belive that the TDD is one of the most important methods of the software development, because it reduces considerably the errors during development. If we add to it the small intervals between iterations of the Agile Software Development techniques, we're able to customize the best product on-demand, i.e. just in time (as soon the new specifications of the problem appear), thereby ensuring a higher quality to the final product. For those more interested, I recommend another reading: On the Effectiveness of Test-first Approach to Programming.