So Test Driven Development starts with a failing test. You may ask why failing, and the answer is simply because there is no code to test against. After our first test fails, it gives us a clue of what code to write in our application to make it pass, and that is how our tests drive the development for us, hence called Test Driven Development.
In the previous article, we generated a model called User and one of the field it had had, is: age . Now, we would like to define a method called adult? that returns true if age of the user is either 18 or greater than it. Before we start writing code for this method, we should start off by writing a failing test first.
So, in our test file located at: test/models/user.rb
require 'test_helper' class UserTest < ActiveSupport::TestCase # test "the truth" do # assert true # end test "it responds to adult?" do user = User.new assert_respond_to user, "adult?", "user doesn't respond to adult?" end end
assert_respond_to takes two arguments, and optionally a third argument. First argument is an object on which the method is called and we provide the method in string format in the second argument. The last argument is actually the message that we would get if our test fails.
The word assert carries a strong significance in test driven development methodology as this method decides either a test passes or not. Assert simply means to state something like a fact or a belief. So in our tests, we present a state that the user object would behave this way, and the testing framework confirms it or rejects it by running the actual code, thus – making the test go green or red respectively.
To run this test, we need to invoke the following command:
This command will run all the tests. Running it in our case produces the following output:
1) Failure: UserTest#test_it_responds_to_adult? [/Users/square63/Projects/first_rails_project /test/models/user_test.rb:11]: user doesn't respond to adult?. Expected #<User id: nil, first_name: nil, last_name: nil, age: nil, created_at: n il, updated_at: nil, postal_code: nil> (User) to respond to #adult?.
The string(message) we passed in the method shows up in the output; yay!
So it’s the time to make the test pass, and we can do so by writing the actual code we had intended to write earlier.
class User < ActiveRecord::Base def adult? end end
Now if we run the test by running the command rake test, it shall pass. That was the second step. Now, in this case, there is no third step as there is nothing to refactor in our code.
Let’s extend it to complete the functionality of adult? function. Again, by writing test first.
require 'test_helper' class UserTest < ActiveSupport::TestCase # test "the truth" do # assert true # end test "it responds to adult?" do user = User.new assert_respond_to user, "adult?", "user doesn't respond to adult?" end test "adult? returns true if the age is greater than or equal to 18" user = User.new user.age = 18 assert user.adult? end end
Run it, and it shall fail.
class User < ActiveRecord::Base def adult? true end end
Yes, we didn’t write the complete functionality; we just wrote the minimum code to pass it. Running again will make the test go green.
So finally it gets us to the third step: refactoring. We would refactor our code, and after we are done refactoring the code, we would again run the tests to make sure that it is working according to what we defined it for.
class User < ActiveRecord::Base ADULT_AGE = 18 def adult? age >= ADULT_AGE end end
So we have refactored our code, and it is the time to run the test, and it shall pass. But here is the thing: we checked just the positive outcome; we also need to check that it returns false if age is less than 18.
test "adult? returns false is age is less than 18" do user = User.new user.age = rand 1..17 assert_not user.adult? end
assert_not checks for the false outcome.
So that’s it for TDD in Rails. Hope you have got the basics of Test Driven Development, and now you are ready to move on. You may head over to Official Rails guide to learn testing in Rails. May your tests go red first, then green and finally you refactor your code and keep on with this cycle: Red-Green-Refactor.