In this part, we will intentionally add bugs to our app, so we can see what some different kinds of test failures look like.
Edit the app/controllers/limericks_controller.rb
file to add a syntax error to the LimericksController#index
method as follows.
class LimericksController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :require_permission, except: [:index, :new, :create]
def index
@limericks = Limerick.all.reverse_order
renxxxxxxder :index
end
...
Note that the call to render
is now an invalid call to a undefined method renxxxxxxder
. Although this example seems contrived, it is not uncommon for a developer to bump the keyboard while working in a file and to not notice the bug they introduced.
Run all tests to check for errors.
rails test -v
The output of this command should look like this:
Running 1 tests in a single process (parallelization threshold is 50)
Run options: -v --seed 61993
# Running:
LimericksControllerTest#test_should_get_index = 0.15 s = E
Error:
LimericksControllerTest#test_should_get_index:
NoMethodError: undefined method `renxxxxxxder' for #<LimericksController:0x0000000000d1d8>
renxxxxxxder :index
^^^^^^^^^^^^
app/controllers/limericks_controller.rb:8:in `index'
test/controllers/limericks_controller_test.rb:6:in `block in <class:LimericksControllerTest>'
rails test test/controllers/limericks_controller_test.rb:4
Finished in 0.153665s, 6.5077 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
Note that error messages now appear in the test output. We can see that the undefined method is mentioned (“NoMethodError: undefined method \
renxxxxxxder'") and that the file and line number where the bug was made is also mentioned ("
app/controllers/limericks_controller.rb:8:in `index’"). Also, note that this bug is classified as an "error", because it essentially made the system crash (unlike a "failure", which is indicative of a failed
assert` call).
Before moving on, correct the above bug and retest the code to ensure that the bug was fixed correctly.
Edit the app/controllers/limericks_controller.rb
file to add a logic bug such that, in the index
method, the call to Limerick.all.reverse_order
is replaced with a call to Limerick.take(1)
(which returns a collection of Limerick
objects containing only 1 limerick).
class LimericksController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :require_permission, except: [:index, :new, :create]
def index
@limericks = Limerick.take(1)
render :index
end
...
Note that we have 2 Limerick
test fixtures, but our app will now retrieve only 1 of them (due to the take(1)
call). In our LimericksControllerTest#should_get_index
test case, the assertion assert_select 'div.card', limericks_size
should now fail because the number of div.card
elements printed will be 1, whereas the limericks_size
value will be 2.
Run all tests to check for errors.
rails test -v
The output of this command should look like this:
Running 1 tests in a single process (parallelization threshold is 50)
Run options: -v --seed 31239
# Running:
LimericksControllerTest#test_should_get_index = 2.24 s = F
Failure:
LimericksControllerTest#test_should_get_index [/Users/sdf/workspace/comp7012/demo/limeriq-f23/test/controllers/limericks_controller_test.rb:8]:
Expected exactly 2 elements matching "div.card", found 1..
Expected: 2
Actual: 1
rails test test/controllers/limericks_controller_test.rb:4
Finished in 2.243783s, 0.4457 runs/s, 0.8914 assertions/s.
1 runs, 2 assertions, 1 failures, 0 errors, 0 skips
Note that the assert_select 'div.card', limericks_size
did indeed fail. The test output reports a failure in the LimericksControllerTest#test_should_get_index
test case on line 8 of the limericks_controller_test.rb
file (where the assert_select
call is found). The output even mentions the cause of the assertion’s failure: “Expected exactly 2 elements matching "div.card", found 1..
”. Note that this bug counts as a “failure” because it was caught by an assertion (and did not cause the system to crash as in the case of an “error”).
Before moving on, correct the above bug and retest the code to ensure that the bug was fixed correctly.
We will now add some errors that our tests fail to detect. The purpose of this exercise is to illustrate some of the risks of testing and to motivate the need to be thorough in writing tests.
Edit the app/controllers/user_limericks_controller.rb
file to add a bug into the index
method as follows.
class UserLimericksController < ApplicationController
before_action :authenticate_user!
def index
@user = User.find(params[:user_id])
@limericks = @user.limericks.reverse_order
renxxxxxxder :index
end
end
Note that, similar to the earlier example, the call to render
is now an invalid call to a undefined method renxxxxxxder
.
Run all tests to check for errors.
rails test -v
The output of this command should look like this:
Running 1 tests in a single process (parallelization threshold is 50)
Run options: -v --seed 389
# Running:
LimericksControllerTest#test_should_get_index = 2.11 s = .
Finished in 2.112802s, 0.4733 runs/s, 0.9466 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
Note that the test did not detect the obvious syntax error! How can this be? If you accidentally add a syntax error into a Ruby script, the error will not be revealed unless the script file with the error is loaded. Rails uses “lazy loading”, which means that script files are loaded only when they are used. In our case above, the file with the syntax error was never used by our test case, and therefore, the file was never loaded. Thus, thorough automated testing (which ensures that all code files are loaded and tested) is particularly important for discovering these sorts of errors.
Before moving on, correct the above bug.
Edit the app/controllers/limericks_controller.rb
file to add a logic bug such that, in the index
method, the call to Limerick.all.reverse_order
is replaced with a call to Limerick.take(2)
(which returns a collection of Limerick
objects containing only 2 limerick).
class LimericksController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :require_permission, except: [:index, :new, :create]
def index
@limericks = Limerick.take(2)
render :index
end
...
Note that this code now clearly contains a logic error—no matter how many limericks are in the database, the app will only ever display 2 of them. However, because we happen to have 2 Limerick
test fixtures, the assertion assert_select 'div.card', limericks_size
will not report a failure. Thus, the logic error will be missed.
Run all tests to check for errors.
rails test -v
The output of this command should look like this:
Running 1 tests in a single process (parallelization threshold is 50)
Run options: -v --seed 389
# Running:
LimericksControllerTest#test_should_get_index = 2.11 s = .
Finished in 2.112802s, 0.4733 runs/s, 0.9466 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
Note that the test indeed misses this logic bug. The purpose of this example is to illustrate, yet again, the importance of writing tests that thoroughly test the code to minimize the number of bugs they might reasonably miss.
Before moving on, correct the above bug.
⏴ Back | Next ⏵ |