diff options
Diffstat (limited to 'guides/source/testing.md')
-rw-r--r-- | guides/source/testing.md | 104 |
1 files changed, 61 insertions, 43 deletions
diff --git a/guides/source/testing.md b/guides/source/testing.md index b5e49a41f4..1c64b2c0ac 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -265,7 +265,7 @@ By now you've caught a glimpse of some of the assertions that are available. Ass Here's an extract of the assertions you can use with [`Minitest`](https://github.com/seattlerb/minitest), the default testing library used by Rails. The `[msg]` parameter is an optional string message you can -specify to make your test failure messages clearer. +specify to make your test failure messages clearer. | Assertion | Purpose | | ---------------------------------------------------------------- | ------- | @@ -316,12 +316,12 @@ Rails adds some custom assertions of its own to the `minitest` framework: | Assertion | Purpose | | --------------------------------------------------------------------------------- | ------- | -| `assert_difference(expressions, difference = 1, message = nil) {...}` | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.| -| `assert_no_difference(expressions, message = nil, &block)` | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.| -| `assert_recognizes(expected_options, path, extras={}, message=nil)` | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| -| `assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)` | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| -| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| -| `assert_redirected_to(options = {}, message=nil)` | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| +| [`assert_difference(expressions, difference = 1, message = nil) {...}`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference) | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.| +| [`assert_no_difference(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference) | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.| +| [`assert_recognizes(expected_options, path, extras={}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes) | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| +| [`assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates) | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| +| [`assert_response(type, message = nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response) | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| +| [`assert_redirected_to(options = {}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to) | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| You'll see the usage of some of these assertions in the next chapter. @@ -329,11 +329,11 @@ You'll see the usage of some of these assertions in the next chapter. All the basic assertions such as `assert_equal` defined in `Minitest::Assertions` are also available in the classes we use in our own test cases. In fact, Rails provides the following classes for you to inherit from: -* `ActiveSupport::TestCase` -* `ActionMailer::TestCase` -* `ActionView::TestCase` -* `ActionDispatch::IntegrationTest` -* `ActiveJob::TestCase` +* [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html) +* [`ActionMailer::TestCase`](http://api.rubyonrails.org/classes/ActionMailer/TestCase.html) +* [`ActionView::TestCase`](http://api.rubyonrails.org/classes/ActionView/TestCase.html) +* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html) +* [`ActiveJob::TestCase`](http://api.rubyonrails.org/classes/ActiveJob/TestCase.html) Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests. @@ -415,6 +415,8 @@ You can find comprehensive documentation in the [Fixtures API documentation](htt _Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and written in YAML. There is one file per model. +NOTE: Fixtures are not designed to create every object that your tests need, and are best managed when only used for default data that can be applied to the common case. + You'll find fixtures under your `test/fixtures` directory. When you run `rails generate model` to create a new model, Rails automatically creates fixture stubs in this directory. #### YAML @@ -518,7 +520,7 @@ create test/models/article_test.rb create test/fixtures/articles.yml ``` -Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from `ActiveSupport::TestCase`. +Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html). Integration Testing @@ -589,7 +591,7 @@ class BlogFlowTest < ActionDispatch::IntegrationTest end ``` -We will take a look at `assert_select` to query the resulting HTML of a request in the "Testing Views" section below. It is used for testing the response of our request by asserting the presence of key HTML elements and their content. +We will take a look at `assert_select` to query the resulting HTML of a request in the "Testing Views" section below. It is used for testing the response of our request by asserting the presence of key HTML elements and their content. When we visit our root path, we should see `welcome/index.html.erb` rendered for the view. So this assertion should pass. @@ -638,9 +640,9 @@ We were able to successfully test a very small workflow for visiting our blog an Functional Tests for Your Controllers ------------------------------------- -In Rails, testing the various actions of a controller is a form of writing functional tests. Remember your controllers handle the incoming web requests to your application and eventually respond with a rendered view. When writing functional tests, you're testing how your actions handle the requests and the expected result, or response in some cases an HTML view. +In Rails, testing the various actions of a controller is a form of writing functional tests. Remember your controllers handle the incoming web requests to your application and eventually respond with a rendered view. When writing functional tests, you are testing how your actions handle the requests and the expected result or response, in some cases an HTML view. -### What to Include in your Functional Tests +### What to include in your Functional Tests You should test for things such as: @@ -650,8 +652,7 @@ You should test for things such as: * was the correct object stored in the response template? * was the appropriate message displayed to the user in the view? -The easiest way to see functional tests in action is to generate a controller -scaffold: +The easiest way to see functional tests in action is to generate a controller using the scaffold generator: ```bash $ bin/rails generate scaffold_controller article title:string body:text @@ -664,7 +665,7 @@ create test/controllers/articles_controller_test.rb ``` This will generate the controller code and tests for an `Article` resource. -You can take look at the file `articles_controller_test.rb` in the `test/controllers` directory. +You can take a look at the file `articles_controller_test.rb` in the `test/controllers` directory. If you already have a controller and just want to generate the test scaffold code for each of the seven default actions, you can use the following command: @@ -677,7 +678,7 @@ create test/controllers/articles_controller_test.rb ... ``` -Let me take you through one such test, `test_should_get_index` from the file `articles_controller_test.rb`. +Let's take a look at one such test, `test_should_get_index` from the file `articles_controller_test.rb`. ```ruby # articles_controller_test.rb @@ -693,7 +694,7 @@ end In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that the right response body has been generated. -The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments: +The `get` method kicks off the web request and populates the results into the `@response`. It accepts 4 arguments: * The action of the controller you are requesting. This can be in the form of a string or a route (i.e. `articles_url`). @@ -705,7 +706,7 @@ The `get` method kicks off the web request and populates the results into the re * `flash`: option with a hash of flash values. -All the keyword arguments are optional. +All of these keyword arguments are optional. Example: Calling the `:show` action, passing an `id` of 12 as the `params` and setting a `user_id` of 5 in the session: @@ -753,7 +754,7 @@ NOTE: Functional tests do not verify whether the specified request type is accep ### Testing XHR (AJAX) requests To test AJAX requests, you can specify the `xhr: true` option to `get`, `post`, -`patch`, `put`, and `delete` methods: +`patch`, `put`, and `delete` methods. For example: ```ruby test "ajax request" do @@ -808,7 +809,7 @@ post article_url # simulate the request with custom env variable ### Testing `flash` notices -If you remember from earlier one of the Three Hashes of the Apocalypse was `flash`. +If you remember from earlier, one of the Three Hashes of the Apocalypse was `flash`. We want to add a `flash` message to our blog application whenever someone successfully creates a new Article. @@ -893,7 +894,7 @@ test "should show article" do end ``` -Remember from our discussion earlier on fixtures the `articles()` method will give us access to our Articles fixtures. +Remember from our discussion earlier on fixtures, the `articles()` method will give us access to our Articles fixtures. How about deleting an existing Article? @@ -913,14 +914,19 @@ We can also add a test for updating an existing Article. ```ruby test "should update article" do article = articles(:one) + patch '/article', params: { id: article.id, article: { title: "updated" } } + assert_redirected_to article_path(article) + # Reload association to fetch updated data and assert that title is updated. + article.reload + assert_equal "updated", article.title end ``` Notice we're starting to see some duplication in these three tests, they both access the same Article fixture data. We can D.R.Y. this up by using the `setup` and `teardown` methods provided by `ActiveSupport::Callbacks`. -Our test should now look something like this, disregard the other tests we're leaving them out for brevity. +Our test should now look something as what follows. Disregard the other tests for now, we're leaving them out for brevity. ```ruby require 'test_helper' @@ -952,8 +958,12 @@ class ArticlesControllerTest < ActionDispatch::IntegrationTest end test "should update article" do - patch article_url(@article), params: { article: { title: "updated" } } + patch '/article', params: { id: @article.id, article: { title: "updated" } } + assert_redirected_to article_path(@article) + # Reload association to fetch updated data and assert that title is updated. + @article.reload + assert_equal "updated", @article.title end end ``` @@ -966,7 +976,7 @@ To avoid code duplication, you can add your own test helpers. Sign in helper can be a good example: ```ruby -test/test_helper.rb +#test/test_helper.rb module SignInHelper def sign_in(user) @@ -999,6 +1009,8 @@ Testing Routes Like everything else in your Rails application, you can test your routes. +NOTE: If your application has complex routes, Rails provides a number of useful helpers to test them. + For more information on routing assertions available in Rails, see the API documentation for [`ActionDispatch::Assertions::RoutingAssertions`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html). Testing Views @@ -1045,7 +1057,7 @@ assert_select "ol" do end ``` -This assertion is quite powerful. For more advanced usage, refer to its [documentation](http://www.rubydoc.info/github/rails/rails-dom-testing). +This assertion is quite powerful. For more advanced usage, refer to its [documentation](https://github.com/rails/rails-dom-testing/blob/master/lib/rails/dom/testing/assertions/selector_assertions.rb). #### Additional View-Based Assertions @@ -1068,27 +1080,31 @@ end Testing Helpers --------------- +A helper is just a simple module where you can define methods which are +available into your views. + In order to test helpers, all you need to do is check that the output of the helper method matches what you'd expect. Tests related to the helpers are located under the `test/helpers` directory. -A helper test looks like so: +Given we have the following helper: ```ruby -require 'test_helper' - -class UserHelperTest < ActionView::TestCase +module UserHelper + def link_to_user(user) + link_to "#{user.first_name} #{user.last_name}", user + end end ``` -A helper is just a simple module where you can define methods which are -available into your views. To test the output of the helper's methods, you just -have to use a mixin like this: +We can test the output of this method like this: ```ruby class UserHelperTest < ActionView::TestCase - test "should return the user name" do - # ... + test "should return the user's full name" do + user = users(:david) + + assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_to_user(user) end end ``` @@ -1123,7 +1139,7 @@ In order to test that your mailer is working as expected, you can use unit tests For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output _should_ look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within `test/fixtures` directly corresponds to the name of the mailer. So, for a mailer named `UserMailer`, the fixtures should reside in `test/fixtures/user_mailer` directory. -When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator you'll have to make those files yourself. +When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator, you'll have to create those files yourself. #### The Basic Test Case @@ -1204,7 +1220,7 @@ Testing Jobs ------------ Since your custom jobs can be queued at different levels inside your application, -you'll need to test both jobs themselves (their behavior when they get enqueued) +you'll need to test both, the jobs themselves (their behavior when they get enqueued) and that other entities correctly enqueue them. ### A Basic Test Case @@ -1252,10 +1268,12 @@ class ProductTest < ActiveJob::TestCase end ``` -Testing Time-Dependent Code ---------------------------- +Additional Testing Resources +---------------------------- + +### Testing Time-Dependent Code -Rails provides inbuilt helper methods that enable you to assert that your time-sensitve code works as expected. +Rails provides built-in helper methods that enable you to assert that your time-sensitive code works as expected. Here is an example using the [`travel_to`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to) helper: |