TDD Basics : Test First Programming
- To learn about Test First Programming
Let's write a simple calculator program driven by test. What statements can you make about the calculator program that is true? How about :
- It should add given two numbers
Let's write a specification for this statement. Create a file called calculator_spec.rb with the following contents:
describe Calculator do it 'should add given two numbers' end
The describe is a RSpec method. In this case, we are describing the behavior of the Calculator class. We are expressing the requirement in the method called it that takes a string as it's argument.
Go to the directory where the spec file resides and run the test like this:
$rspec calculator_spec.rb --color --format documentation
This test fails.
Define Calculator class at the top of the calculator_spec.rb file with the code shown below:
class Calculator end describe Calculator do it 'should add given two numbers' end
You now see a pending spec. This is because you have not written the test yet.
Let's write the test as follows:
class Calculator end describe Calculator do it 'should add given two numbers' do calculator = Calculator.new result = calculator.add(1,2) expect(result).to eq(3) end end
The describe() is a method that takes the class name as its argument. The test is written inside the do-end block of the it method. The it() method takes a string argument. This string argument is called the doc string and describes the scenario we are testing.
We first create an instance of the Calculator class. The second step is invoking the method add(x, y) to calculate sum of two numbers. The third step is checking if the result is the same as we expect. In this step, we have converted the statement that is true to an assertion. The expect() method is the assertion in rspec. It takes an argument that needs to be verified for correctness.
Follow the step 2 instructions to run the test again. This test fails because you have not defined the add(x,y) method.
Define the add method to the class as follows:
class Calculator def add(x,y) end end describe Calculator do # The code here is same as before. end
Follow the step 2 instructions to run the test again. We now have a test that is failing for the right reason. Failing for the right reason means that the test will fail to satisfy the requirements instead of syntax mistakes, missing require statements etc.
Change the add to return : x + y. Now the test passes.
You can now move the Calculator class to its own file called calculator.rb.
to the top of the calculator_spec.rb. So the complete code listing for calculator.rb looks like this:
require_relative 'calculator' class Calculator def add(x,y) x + y end end
Run the test again. It should now pass.
In this article we took little baby steps. We wrote the test first. Initial error messages were related to setting up the environment. Once you get past that, you can make the test fail for the right reason. Then you implement the code to make the test pass. Once you get to green you can cleanup the test and the production code. This is called as refactoring. We will discuss more about refactoring in another article.
Why do we need to make the test fail for the right reason?
Test does not have a test to prove its correctness. The correctness of the test is done manually, just once by making sure that it fails for the right reason. Strive to make the test so simple that it does not require a test.
Why did we define the Calculator class in the same file as the test?
We did that for convenience, we were able to see all the code at once. You don't have to have them in the same file. You could have split panes in your IDE showing both files so that it is easy to write code.
Write specs for the following statements:
- It should subtract given two numbers
- It should multiply given two numbers
- It should divide given two numbers
- It should subtract given two numbers
Write specs for edge cases such as invalid input, division by 0 etc.
Create a .rspec file with the following contents
--color --format documentation
Now you can run the specs without giving it any options like this:
What do you see as the output in the terminal?