aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/testing.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/testing.md')
-rw-r--r--guides/source/testing.md440
1 files changed, 363 insertions, 77 deletions
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 34c831c802..b82ccebe7c 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1,14 +1,14 @@
**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
-A Guide to Testing Rails Applications
-=====================================
+Testing Rails Applications
+==========================
This guide covers built-in mechanisms in Rails for testing your application.
After reading this guide, you will know:
* Rails testing terminology.
-* How to write unit, functional, and integration tests for your application.
+* How to write unit, functional, integration, and system tests for your application.
* Other popular testing approaches and plugins.
--------------------------------------------------------------------------------
@@ -18,7 +18,7 @@ Why Write Tests for your Rails Applications?
Rails makes it super easy to write your tests. It starts by producing skeleton test code while you are creating your models and controllers.
-By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
+By running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
@@ -33,16 +33,27 @@ Rails creates a `test` directory for you as soon as you create a Rails project u
```bash
$ ls -F test
-controllers/ helpers/ mailers/ test_helper.rb
-fixtures/ integration/ models/
+controllers/ helpers/ mailers/ system/ test_helper.rb
+fixtures/ integration/ models/ application_system_test_case.rb
```
-The `models` directory is meant to hold tests for your models, the `controllers` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting. There is also a directory for testing your mailers and one for testing view helpers.
+The `helpers`, `mailers`, and `models` directories are meant to hold tests for view helpers, mailers, and models, respectively. The `controllers` directory is meant to hold tests for controllers, routes, and views. The `integration` directory is meant to hold tests for interactions between controllers.
+
+The system test directory holds system tests, which are used for full browser
+testing of your application. System tests allow you to test your application
+the way your users experience it and help you test your JavaScript as well.
+System tests inherit from Capybara and perform in browser tests for your
+application.
Fixtures are a way of organizing test data; they reside in the `fixtures` directory.
+A `jobs` directory will also be created when an associated test is first generated.
+
The `test_helper.rb` file holds the default configuration for your tests.
+The `application_system_test_case.rb` holds the default configuration for your system
+tests.
+
### The Test Environment
@@ -112,7 +123,7 @@ def test_the_truth
end
```
-However only the `test` macro allows a more readable test name. You can still use regular method definitions though.
+Although you can still use regular method definitions, using the `test` macro allows for a more readable test name.
NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. This may require use of `define_method` and `send` calls to function properly, but formally there's little restriction on the name.
@@ -146,18 +157,28 @@ Let us run this newly added test (where `6` is the number of line where the test
```bash
$ bin/rails test test/models/article_test.rb:6
+Run options: --seed 44656
+
+# Running:
+
F
-Finished tests in 0.044632s, 22.4054 tests/s, 22.4054 assertions/s.
+Failure:
+ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/article_test.rb:6]:
+Expected true to be nil or false
+
+
+bin/rails test test/models/article_test.rb:6
+
- 1) Failure:
-test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
-Failed assertion, no message given.
-1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
+Finished in 0.023918s, 41.8090 runs/s, 41.8090 assertions/s.
+
+1 runs, 1 assertions, 1 failures, 0 errors, 0 skips
+
```
-In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message that mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here:
+In the output, `F` denotes a failure. You can see the corresponding trace shown under `Failure` along with the name of the failing test. The next few lines contain the stack trace followed by a message that mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here:
```ruby
test "should not save article without title" do
@@ -169,8 +190,8 @@ end
Running this test shows the friendlier assertion message:
```bash
- 1) Failure:
-test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
+Failure:
+ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/article_test.rb:6]:
Saved the article without a title
```
@@ -186,11 +207,15 @@ Now the test should pass. Let us verify by running the test again:
```bash
$ bin/rails test test/models/article_test.rb:6
+Run options: --seed 31252
+
+# Running:
+
.
-Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s.
+Finished in 0.027476s, 36.3952 runs/s, 36.3952 assertions/s.
-1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
```
Now, if you noticed, we first wrote a test which fails for a desired
@@ -215,16 +240,25 @@ Now you can see even more output in the console from running the tests:
```bash
$ bin/rails test test/models/article_test.rb
-E
+Run options: --seed 1808
+
+# Running:
-Finished tests in 0.030974s, 32.2851 tests/s, 0.0000 assertions/s.
+.E
- 1) Error:
-test_should_report_error(ArticleTest):
-NameError: undefined local variable or method `some_undefined_variable' for #<ArticleTest:0x007fe32e24afe0>
- test/models/article_test.rb:10:in `block in <class:ArticleTest>'
+Error:
+ArticleTest#test_should_report_error:
+NameError: undefined local variable or method 'some_undefined_variable' for #<ArticleTest:0x007fee3aa71798>
+ test/models/article_test.rb:11:in 'block in <class:ArticleTest>'
-1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
+
+bin/rails test test/models/article_test.rb:9
+
+
+
+Finished in 0.040609s, 49.2500 runs/s, 24.6250 assertions/s.
+
+2 runs, 1 assertions, 0 failures, 1 errors, 0 skips
```
Notice the 'E' in the output. It denotes a test with error.
@@ -239,7 +273,7 @@ When a test fails you are presented with the corresponding backtrace. By default
Rails filters that backtrace and will only print lines relevant to your
application. This eliminates the framework noise and helps to focus on your
code. However there are situations when you want to see the full
-backtrace. Simply set the `-b` (or `--backtrace`) argument to enable this behavior:
+backtrace. Set the `-b` (or `--backtrace`) argument to enable this behavior:
```bash
$ bin/rails test -b test/models/article_test.rb
@@ -285,9 +319,10 @@ specify to make your test failure messages clearer.
| `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.|
| `assert_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.|
| `assert_not_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
+| `assert_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` have a relative error less than `epsilon`.|
+| `assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` don't have a relative error less than `epsilon`.|
| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.|
| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.|
-| `assert_nothing_raised { block }` | Ensures that the given block doesn't raise any exceptions.|
| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.|
| `assert_not_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.|
| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class` or is descending from it.|
@@ -298,7 +333,6 @@ specify to make your test failure messages clearer.
| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
| `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`|
| `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`|
-| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true, e.g. assert_send [@user, :full_name, 'Sam Smith']. This one is weird eh?|
| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.|
The above are a subset of assertions that minitest supports. For an exhaustive &
@@ -318,6 +352,9 @@ Rails adds some custom assertions of its own to the `minitest` framework:
| --------------------------------------------------------------------------------- | ------- |
| [`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_changes(expressions, message = nil, from:, to:, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes) | Test that the result of evaluating an expression is changed after invoking the passed in block.|
+| [`assert_no_changes(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes) | Test the result of evaluating an expression is not changed after invoking the passed in block.|
+| [`assert_nothing_raised { block }`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised) | Ensures that the given block doesn't raise any exceptions.|
| [`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.|
@@ -332,8 +369,10 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions
* [`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)
+* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html)
+* [`ActionDispatch::SystemTestCase`](http://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html)
+* [`Rails::Generators::TestCase`](http://api.rubyonrails.org/classes/Rails/Generators/TestCase.html)
Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
@@ -342,17 +381,21 @@ documentation](http://docs.seattlerb.org/minitest).
### The Rails Test Runner
-We can run all of our tests at once by using the `rails test` command.
+We can run all of our tests at once by using the `bin/rails test` command.
-Or we can run a single test by passing the `rails test` command the filename containing the test cases.
+Or we can run a single test file by passing the `bin/rails test` command the filename containing the test cases.
```bash
$ bin/rails test test/models/article_test.rb
-.
+Run options: --seed 1559
+
+# Running:
-Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
+..
-1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+Finished in 0.027034s, 73.9810 runs/s, 110.9715 assertions/s.
+
+2 runs, 3 assertions, 0 failures, 0 errors, 0 skips
```
This will run all test methods from the test case.
@@ -362,6 +405,10 @@ You can also run a particular test method from the test case by providing the
```bash
$ bin/rails test test/models/article_test.rb -n test_the_truth
+Run options: -n test_the_truth --seed 43583
+
+# Running:
+
.
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
@@ -372,7 +419,7 @@ Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
You can also run a test at a specific line by providing the line number.
```bash
-$ bin/rails test test/models/post_test.rb:44 # run specific test and line
+$ bin/rails test test/models/article_test.rb:6 # run specific test and line
```
You can also run an entire directory of tests by providing the path to the directory.
@@ -381,6 +428,39 @@ You can also run an entire directory of tests by providing the path to the direc
$ bin/rails test test/controllers # run all tests from specific directory
```
+The test runner also provides a lot of other features like failing fast, deferring test output
+at the end of test run and so on. Check the documentation of the test runner as follows:
+
+```bash
+$ bin/rails test -h
+minitest options:
+ -h, --help Display this help.
+ -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
+ -v, --verbose Verbose. Show progress processing files.
+ -n, --name PATTERN Filter run on /regexp/ or string.
+ --exclude PATTERN Exclude /regexp/ or string from run.
+
+Known extensions: rails, pride
+
+Usage: bin/rails test [options] [files or directories]
+You can run a single test by appending a line number to a filename:
+
+ bin/rails test test/models/user_test.rb:27
+
+You can run multiple files and directories at the same time:
+
+ bin/rails test test/controllers test/integration/login_test.rb
+
+By default test failures and errors are reported inline during a run.
+
+Rails options:
+ -w, --warnings Run with Ruby warnings enabled
+ -e, --environment Run tests in the ENV environment
+ -b, --backtrace Show the complete backtrace
+ -d, --defer-output Output test failures and errors after the test run
+ -f, --fail-fast Abort test run on first failure or error
+ -c, --[no-]color Enable color in the output
+```
The Test Database
-----------------
@@ -440,7 +520,7 @@ steve:
Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank line. You can place comments in a fixture file by using the # character in the first column.
-If you are working with [associations](/association_basics.html), you can simply
+If you are working with [associations](/association_basics.html), you can
define a reference node between two different fixtures. Here's an example with
a `belongs_to`/`has_many` association:
@@ -523,13 +603,199 @@ create test/fixtures/articles.yml
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).
+System Testing
+--------------
+
+System tests allow you to test user interactions with your application, running tests
+in either a real or a headless browser. System tests uses Capybara under the hood.
+
+For creating Rails system tests, you use the `test/system` directory in your
+application. Rails provides a generator to create a system test skeleton for you.
+
+```bash
+$ bin/rails generate system_test users
+ invoke test_unit
+ create test/system/users_test.rb
+```
+
+Here's what a freshly generated system test looks like:
+
+```ruby
+require "application_system_test_case"
+
+class UsersTest < ApplicationSystemTestCase
+ # test "visiting the index" do
+ # visit users_url
+ #
+ # assert_selector "h1", text: "Users"
+ # end
+end
+```
+
+By default, system tests are run with the Selenium driver, using the Chrome
+browser, and a screen size of 1400x1400. The next section explains how to
+change the default settings.
+
+### Changing the default settings
+
+Rails makes changing the default settings for system tests very simple. All
+the setup is abstracted away so you can focus on writing your tests.
+
+When you generate a new application or scaffold, an `application_system_test_case.rb` file
+is created in the test directory. This is where all the configuration for your
+system tests should live.
+
+If you want to change the default settings you can change what the system
+tests are "driven by". Say you want to change the driver from Selenium to
+Poltergeist. First add the `poltergeist` gem to your `Gemfile`. Then in your
+`application_system_test_case.rb` file do the following:
+
+```ruby
+require "test_helper"
+require "capybara/poltergeist"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :poltergeist
+end
+```
+
+The driver name is a required argument for `driven_by`. The optional arguments
+that can be passed to `driven_by` are `:using` for the browser (this will only
+be used by Selenium), `:screen_size` to change the size of the screen for
+screenshots, and `:options` which can be used to set options supported by the
+driver.
+
+```ruby
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :firefox
+end
+```
+
+If you want to use a headless browser, you could use Headless Chrome or Headless Firefox by adding
+`headless_chrome` or `headless_firefox` in the `:using` argument.
+
+```ruby
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :headless_chrome
+end
+```
+
+If your Capybara configuration requires more setup than provided by Rails, this
+additional configuration could be added into the `application_system_test_case.rb`
+file.
+
+Please see [Capybara's documentation](https://github.com/teamcapybara/capybara#setup)
+for additional settings.
+
+### Screenshot Helper
+
+The `ScreenshotHelper` is a helper designed to capture screenshots of your tests.
+This can be helpful for viewing the browser at the point a test failed, or
+to view screenshots later for debugging.
+
+Two methods are provided: `take_screenshot` and `take_failed_screenshot`.
+`take_failed_screenshot` is automatically included in `after_teardown` inside
+Rails.
+
+The `take_screenshot` helper method can be included anywhere in your tests to
+take a screenshot of the browser.
+
+### Implementing a system test
+
+Now we're going to add a system test to our blog application. We'll demonstrate
+writing a system test by visiting the index page and creating a new blog article.
+
+If you used the scaffold generator, a system test skeleton was automatically
+created for you. If you didn't use the scaffold generator, start by creating a
+system test skeleton.
+
+```bash
+$ bin/rails generate system_test articles
+```
+
+It should have created a test file placeholder for us. With the output of the
+previous command you should see:
+
+```bash
+ invoke test_unit
+ create test/system/articles_test.rb
+```
+
+Now let's open that file and write our first assertion:
+
+```ruby
+require "application_system_test_case"
+
+class ArticlesTest < ApplicationSystemTestCase
+ test "viewing the index" do
+ visit articles_path
+ assert_selector "h1", text: "Articles"
+ end
+end
+```
+
+The test should see that there is an `h1` on the articles index page and pass.
+
+Run the system tests.
+
+```bash
+bin/rails test:system
+```
+
+NOTE: By default, running `bin/rails test` won't run your system tests.
+Make sure to run `bin/rails test:system` to actually run them.
+
+#### Creating articles system test
+
+Now let's test the flow for creating a new article in our blog.
+
+```ruby
+test "creating an article" do
+ visit articles_path
+
+ click_on "New Article"
+
+ fill_in "Title", with: "Creating an Article"
+ fill_in "Body", with: "Created this article successfully!"
+
+ click_on "Create Article"
+
+ assert_text "Creating an Article"
+end
+```
+
+The first step is to call `visit articles_path`. This will take the test to the
+articles index page.
+
+Then the `click_on "New Article"` will find the "New Article" button on the
+index page. This will redirect the browser to `/articles/new`.
+
+Then the test will fill in the title and body of the article with the specified
+text. Once the fields are filled in, "Create Article" is clicked on which will
+send a POST request to create the new article in the database.
+
+We will be redirected back to the articles index page and there we assert
+that the text from the new article's title is on the articles index page.
+
+#### Taking it further
+
+The beauty of system testing is that it is similar to integration testing in
+that it tests the user's interaction with your controller, model, and view, but
+system testing is much more robust and actually tests your application as if
+a real user were using it. Going forward, you can test anything that the user
+themselves would do in your application such as commenting, deleting articles,
+publishing draft articles, etc.
Integration Testing
-------------------
Integration tests are used to test how various parts of your application interact. They are generally used to test important workflows within our application.
-For creating Rails integration tests, we use the 'test/integration' directory for our application. Rails provides a generator to create an integration test skeleton for us.
+For creating Rails integration tests, we use the `test/integration` directory for our application. Rails provides a generator to create an integration test skeleton for us.
```bash
$ bin/rails generate integration_test user_flows
@@ -537,7 +803,7 @@ $ bin/rails generate integration_test user_flows
create test/integration/user_flows_test.rb
```
-Here's what a freshly-generated integration test looks like:
+Here's what a freshly generated integration test looks like:
```ruby
require 'test_helper'
@@ -675,7 +941,7 @@ each of the seven default actions, you can use the following command:
$ bin/rails generate test_unit:scaffold article
...
invoke test_unit
-create test/controllers/articles_controller_test.rb
+create test/controllers/articles_controller_test.rb
...
```
@@ -685,9 +951,8 @@ Let's take a look at one such test, `test_should_get_index` from the file `artic
# articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
- get '/articles'
+ get articles_url
assert_response :success
- assert_includes @response.body, 'Articles'
end
end
```
@@ -695,30 +960,29 @@ 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 action of the controller you are requesting.
- This can be in the form of a string or a route (i.e. `articles_url`).
+The `get` method kicks off the web request and populates the results into the `@response`. It can accept up to 6 arguments:
+* The URI of the controller action you are requesting.
+ This can be in the form of a string or a route helper (e.g. `articles_url`).
* `params`: option with a hash of request parameters to pass into the action
(e.g. query string parameters or article variables).
-
-* `session`: option with a hash of session variables to pass along with the request.
-
-* `flash`: option with a hash of flash values.
+* `headers`: for setting the headers that will be passed with the request.
+* `env`: for customizing the request environment as needed.
+* `xhr`: whether the request is Ajax request or not. Can be set to true for marking the request as Ajax.
+* `as`: for encoding the request with different content type. Supports `:json` by default.
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:
+Example: Calling the `:show` action, passing an `id` of 12 as the `params` and setting `HTTP_REFERER` header:
```ruby
-get(:show, params: { id: 12 }, session: { user_id: 5 })
+get article_url, params: { id: 12 }, headers: { "HTTP_REFERER" => "http://example.com/home" }
```
-Another example: Calling the `:view` action, passing an `id` of 12 as the `params`, this time with no session, but with a flash message.
+Another example: Calling the `:update` action, passing an `id` of 12 as the `params` as an Ajax request.
```ruby
-get(view_url, params: { id: 12 }, flash: { message: 'booya!' })
+patch article_url, params: { id: 12 }, xhr: true
```
NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
@@ -728,7 +992,7 @@ Let us modify `test_should_create_article` test in `articles_controller_test.rb`
```ruby
test "should create article" do
assert_difference('Article.count') do
- post '/article', params: { article: { title: 'Some title' } }
+ post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }
end
assert_redirected_to article_path(Article.last)
@@ -737,6 +1001,13 @@ end
Now you can try running all the tests and they should pass.
+NOTE: If you followed the steps in the Basic Authentication section, you'll need to add the following to the `setup` block to get all the tests passing:
+
+```ruby
+request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
+ encode_credentials('dhh', 'secret')
+```
+
### Available Request Types for Functional Tests
If you're familiar with the HTTP protocol, you'll know that `get` is a type of request. There are 6 request types supported in Rails functional tests:
@@ -759,7 +1030,7 @@ To test AJAX requests, you can specify the `xhr: true` option to `get`, `post`,
```ruby
test "ajax request" do
- article = articles(:first)
+ article = articles(:one)
get article_url(article), xhr: true
assert_equal 'hello world', @response.body
@@ -785,27 +1056,38 @@ cookies["are_good_for_u"] cookies[:are_good_for_u]
### Instance Variables Available
-You also have access to three instance variables in your functional tests:
+You also have access to three instance variables in your functional tests, after a request is made:
* `@controller` - The controller processing the request
* `@request` - The request object
* `@response` - The response object
+
+```ruby
+class ArticlesControllerTest < ActionDispatch::IntegrationTest
+ test "should get index" do
+ get articles_url
+
+ assert_equal "index", @controller.action_name
+ assert_equal "application/x-www-form-urlencoded", @request.media_type
+ assert_match "Articles", @response.body
+ end
+end
+```
+
### Setting Headers and CGI variables
-[HTTP headers](http://tools.ietf.org/search/rfc2616#section-5.3)
+[HTTP headers](https://tools.ietf.org/search/rfc2616#section-5.3)
and
-[CGI variables](http://tools.ietf.org/search/rfc3875#section-4.1)
-can be set directly on the `@request` instance variable:
+[CGI variables](https://tools.ietf.org/search/rfc3875#section-4.1)
+can be passed as headers:
```ruby
# setting an HTTP Header
-@request.headers["Accept"] = "text/plain, text/html"
-get articles_url # simulate the request with custom header
+get articles_url, headers: { "Content-Type": "text/plain" } # simulate the request with custom header
# setting a CGI variable
-@request.headers["HTTP_REFERER"] = "http://example.com/home"
-post article_url # simulate the request with custom env variable
+get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" } # simulate the request with custom env variable
```
### Testing `flash` notices
@@ -841,7 +1123,7 @@ F
Finished in 0.114870s, 8.7055 runs/s, 34.8220 assertions/s.
1) Failure:
-ArticlesControllerTest#test_should_create_article [/Users/zzak/code/bench/sharedapp/test/controllers/articles_controller_test.rb:16]:
+ArticlesControllerTest#test_should_create_article [/test/controllers/articles_controller_test.rb:16]:
--- expected
+++ actual
@@ -1 +1 @@
@@ -890,7 +1172,7 @@ Let's write a test for the `:show` action:
```ruby
test "should show article" do
article = articles(:one)
- get '/article', params: { id: article.id }
+ get article_url(article)
assert_response :success
end
```
@@ -916,7 +1198,7 @@ We can also add a test for updating an existing Article.
test "should update article" do
article = articles(:one)
- patch '/article', params: { id: article.id, article: { title: "updated" } }
+ patch article_url(article), params: { article: { title: "updated" } }
assert_redirected_to article_path(article)
# Reload association to fetch updated data and assert that title is updated.
@@ -959,7 +1241,7 @@ class ArticlesControllerTest < ActionDispatch::IntegrationTest
end
test "should update article" do
- patch '/article', params: { id: @article.id, article: { title: "updated" } }
+ patch article_url(@article), params: { article: { title: "updated" } }
assert_redirected_to article_path(@article)
# Reload association to fetch updated data and assert that title is updated.
@@ -977,11 +1259,11 @@ 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)
- session[:user_id] = user.id
+ def sign_in_as(user)
+ post sign_in_url(email: user.email, password: user.password)
end
end
@@ -997,7 +1279,7 @@ class ProfileControllerTest < ActionDispatch::IntegrationTest
test "should show profile" do
# helper is now reusable from any controller test case
- sign_in users(:david)
+ sign_in_as users(:david)
get profile_url
assert_response :success
@@ -1008,7 +1290,7 @@ end
Testing Routes
--------------
-Like everything else in your Rails application, you can test your routes.
+Like everything else in your Rails application, you can test your routes. Route tests reside in `test/controllers/` or are part of controller tests.
NOTE: If your application has complex routes, Rails provides a number of useful helpers to test them.
@@ -1140,7 +1422,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 create those files yourself.
+If you generated your mailer, the generator does not create stub fixtures for the mailers actions. You'll have to create those files yourself as described above.
#### The Basic Test Case
@@ -1174,6 +1456,10 @@ variable. We then ensure that it was sent (the first assert), then, in the
second batch of assertions, we ensure that the email does indeed contain what we
expect. The helper `read_fixture` is used to read in the content from this file.
+NOTE: `email.body.to_s` is present when there's only one (HTML or text) part present.
+If the mailer provides both, you can test your fixture against specific parts
+with `email.text_part.body.to_s` or `email.html_part.body.to_s`.
+
Here's the content of the `invite` fixture:
```
@@ -1212,7 +1498,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest
assert_equal "You have been invited by me@example.com", invite_email.subject
assert_equal 'friend@example.com', invite_email.to[0]
- assert_match(/Hi friend@example.com/, invite_email.body.to_s)
+ assert_match(/Hi friend@example\.com/, invite_email.body.to_s)
end
end
```
@@ -1240,7 +1526,7 @@ class BillingJobTest < ActiveJob::TestCase
end
```
-This test is pretty simple and only asserts that the job get the work done
+This test is pretty simple and only asserts that the job got the work done
as expected.
By default, `ActiveJob::TestCase` will set the queue adapter to `:test` so that
@@ -1283,11 +1569,11 @@ Here is an example using the [`travel_to`](http://api.rubyonrails.org/classes/Ac
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
- assert_equal Date.new(2004, 10, 24), user.activation_date # inside the travel_to block `Date.current` is mocked
+ assert_equal Date.new(2004, 10, 24), user.activation_date # inside the `travel_to` block `Date.current` is mocked
assert user.applicable_for_gifting?
end
assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block.
```
-Please see [`ActiveSupport::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
+Please see [`ActiveSupport::Testing::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
for in-depth information about the available time helpers.