What is TDD? TDD stands for Test Driven Development.
What is Test Driven Development?
When tests are written to specify future code's behavior, it's called "Test-Driven Development" or TDD.
When we follow our code development based on TDD, the advantages are many.
The main one being production of quality code in optimal amount of time, reducing the time-to-market (TTM).
Rails makes it easy to write tests. It starts by producing skeleton test code while we are creating our models and controllers. By simply running our Rails tests we can ensure our code adheres to the desired functionality even after some major code refactoring.
TDD is three step process. Red, Green , Refactor.
First write a test for a functionality which does not exist. Make sure the test fails(Red). Create the functionality so that the test passes(Green). Then refactor the code to make it cleaner , maintainable and robust.
Narrow the scope of the test to be as small and self-contained as possible. When you are testing the method, only assess what the code returns. Write DRY(Do not Repeat Yourself) test.
One of the most popular testing frameworks in Ruby on Rails community, for TDD is RSpec.
rspec-rails: Supports using RSpec to test Ruby on Rails applications in place of Rails' built-in test framework.
Reading RSpec Tests
RSpec, like most testing frameworks, runs our code in particular conditions and with particular arguments. It sets expectations for the outcome of this process, and the test passes if those expectations are met.
Consider the following code in file example1_spec.rb,
The strings following 'describe' and 'it' are for human read. We can put whatever we want there — though it should actually describe the method and its behavior. Changing the contents of those strings will not change the test behavior.
Following a typical RSpec pattern, The text to the right of describe ("hello") is a description of the method we will be testing. The string "says hello to someone" following it is similarly explanatory, and gives a basic human-readable description of the behavior we intend to test.
If we analyze what the above code is doing:
We declare what we're testing (the hello method), in between describe and do.
We declare our expected behavior in English (for humans), in between the it and the do.
We write the actual test, creating the object which contains the method hello and invoking the function on that object and checking the return value of this method to equal to an expected string. If that expectation is fulfilled, the test will pass; otherwise, it will fail. We "close" the describe and it "blocks" with the keyword end.
RSpec assesses the return value.
To make this test pass, we would implement the hello method as it's written below in example1.rb:
The following is the result of running the RSpec test.
The code below prints 9 when run, though expected result is 20. As can be seen it is not giving error. So we need to have tests created to make sure the code behaves as expected.
The following Test code, should test the above function, mult (example2_spec.rb).
Now if we modify the function, as below, The above test code will pass.
But the above implementation will fail, if the test code is as below:
The mult function has to be as below, (example2.rb) for it to produce correct result for all arguments and pass the tests.
Now when we run RSpec tests on this, they will pass as below:
Narrow implementations of methods can pass single specific tests. We need tests to test code from a variety of angles. We need to read the test specs to make sure we understand exactly what is expected of our code, and what is needed to pass.
Testing Models Using RSpec
First thing we will do is, install the RSpec gem for rspec-core, rspec-expectations and rspec-mocks.
We can create a directory to run our tests. Then we can have two directories, models and spec(RSpec directory). Create two files, entry.rb and entry_spec.rb in models and spec directory respectively.
Let us have the contents of entry_spec.rb as below and contents of the model file, entry.rb empty:
entry_spec.rb uses require_relative to load our entry model for testing. Now when we run rspec command on it, we get the following output:
The '(NameError)' says that the spec failed when we executed it. This is because we reference Entry in the spec, but Entry is not defined anywhere in the current directory. Let's define Entry in models/entry.rb by creating a stub of the class.
Now if we run the rspec test again, we see the following:
As we have not added any test to entry_spec.rb, rspec is telling us 'No examples found'.
Add some tests to test file.
Let us add tests to test the attributes, name, phone_number and email.
RSpec provides methods, expect , to and respond_to etc. This is called Domain Specific Language(DSL). Now when we run the tests we see 3 failures, as we have not provided the attributes in entry.rb as yet.
Now add the attributes to entry.rb as below:
Now we run the tests again, to see no failures.
Create the tests for the entries with values.
When we run the tests again, we get failures:
The failures are because we haven't added intialize method that takes three arguments.
Now add the initialize method with three arguments.
When you run the tests again, the tests pass.
Testing the attributes
We will add tests to read the attribute values. An entry method is used using let helper method, instead of having an entry local variable for each test. The following is the code snippet for entry_spec.rb:
code for entry.rb:
Now when we run the tests again the output will be as below:
RSpec empties the Test database before running each spec. Each test must create the data it needs.
Conclusion
We have seen RSpec test implementation for simple hello method and multiplication methods. We have also seen how to test models using RSpec. We have seen how the tests are written step by step and how the code to be tested is modified accordingly.
Writing TDD code is not that complicated in Ruby on Rails as we have seen right? We can practice to implement it in all our sofware designs to save our companies big money.
References
(1)Bloc Tutorial Material
(2)rubyonrails.org
(3)rspec.info
(4)https://relishapp.com/rspec
(5)https://bparanj.wistia.com/projects/azxn2s3fnq