From 7985908beac7a625f545097dba9460c4355d70e4 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Fri, 14 Aug 2015 11:04:48 -0400 Subject: reorganize testing guide. [Zachary Scott & Yves Senn] [ci skip] Better reading flow for the information presented in this guide. The first part is written in a similar fashion as the "Getting Started Guide" and can be read from start to finish. The second section introduces the different testing components that Rails provides and explains how and when to use them. The guide is still work in progress. --- guides/source/testing.md | 796 +++++++++++++++++++++++------------------------ 1 file changed, 386 insertions(+), 410 deletions(-) (limited to 'guides') diff --git a/guides/source/testing.md b/guides/source/testing.md index 0f24bd0311..3a691220cf 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -25,15 +25,7 @@ Rails tests can also simulate browser requests and thus you can test your applic Introduction to Testing ----------------------- -Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. Just about every Rails application interacts heavily with a database and, as a result, your tests will need a database to interact with as well. To write efficient tests, you'll need to understand how to set up this database and populate it with sample data. - -### The Test Environment - -By default, every Rails application has three environments: development, test, and production. The database for each one of them is configured in `config/database.yml`. - -A dedicated test database allows you to set up and interact with test data in isolation. This way your tests can mangle test data with confidence, without worrying about the data in the development or production databases. - -Also, each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`. +Testing support was woven into the Rails fabric from the beginning. It wasn't an "oh! let's bolt on support for running tests because they're new and cool" epiphany. ### Rails Sets up for Testing from the Word Go @@ -51,123 +43,18 @@ Fixtures are a way of organizing test data; they reside in the `fixtures` direct The `test_helper.rb` file holds the default configuration for your tests. -### The Low-Down on Fixtures - -For good tests, you'll need to give some thought to setting up test data. -In Rails, you can handle this by defining and customizing fixtures. -You can find comprehensive documentation in the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). - -#### What Are Fixtures? - -_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. - -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 - -YAML-formatted fixtures are a human-friendly way to describe your sample data. These types of fixtures have the **.yml** file extension (as in `users.yml`). - -Here's a sample YAML fixture file: - -```yaml -# lo & behold! I am a YAML comment! -david: - name: David Heinemeier Hansson - birthday: 1979-10-15 - profession: Systems development - -steve: - name: Steve Ross Kellock - birthday: 1974-09-27 - profession: guy with keyboard -``` - -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 -define a reference node between two different fixtures. Here's an example with -a `belongs_to`/`has_many` association: - -```yaml -# In fixtures/categories.yml -about: - name: About - -# In fixtures/articles.yml -one: - title: Welcome to Rails! - body: Hello world! - category: about -``` - -Notice the `category` key of the `one` article found in `fixtures/articles.yml` has a value of `about`. This tells Rails to load the category `about` found in `fixtures/categories.yml`. - -NOTE: For associations to reference one another by name, you cannot specify the `id:` attribute on the associated fixtures. Rails will auto assign a primary key to be consistent between runs. For more information on this association behavior please read the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). - -#### ERB'in It Up - -ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users: - -```erb -<% 1000.times do |n| %> -user_<%= n %>: - username: <%= "user#{n}" %> - email: <%= "user#{n}@example.com" %> -<% end %> -``` - -#### Fixtures in Action - -Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps: - -1. Remove any existing data from the table corresponding to the fixture -2. Load the fixture data into the table -3. Dump the fixture data into a method in case you want to access it directly - -TIP: In order to remove existing data from the database, Rails tries to disable referential integrity triggers (like foreign keys and check constraints). If you are getting annoying permission errors on running tests, make sure the database user has privilege to disable these triggers in testing environment. (In PostgreSQL, only superusers can disable all triggers. Read more about PostgreSQL permissions [here](http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html)) - -#### Fixtures are Active Record objects - -Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method whose scope is local of the test case. For example: - -```ruby -# this will return the User object for the fixture named david -users(:david) -# this will return the property for david called id -users(:david).id - -# one can also access methods available on the User class -email(david.partner.email, david.location_tonight) -``` - -To get multiple fixtures at once, you can pass in a list of fixture names. For example: - -```ruby -# this will return an array containing the fixtures david and steve -users(:david, :steve) -``` - -### Console Tasks for Running your Tests - -Rails comes with a CLI command to run tests. -Here are some examples of how to use it: +### The Test Environment -```bash -$ bin/rails test # run all tests in the `test` directory -$ bin/rails test test/controllers # run all tests from specific directory -$ bin/rails test test/models/post_test.rb # run specific test -$ bin/rails test test/models/post_test.rb:44 # run specific test and line -``` +By default, every Rails application has three environments: development, test, and production. -We will cover each of types Rails tests listed above in this guide. +Each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`. -Model Testing ------------------------- +NOTE: Your test are run under RAILS_ENV=test. -For this guide we will be using the application we built in the [Getting Started with Rails](getting_started.html) guide. +### Rails meets Minitest -If you remember when you used the `rails generate scaffold` command from earlier. We created our first resource among other things it created a test stub in the `test/models` directory: +If you remember when you used the `rails generate scaffold` command from the [Getting Started with Rails](getting_started.html) guide. We created our first resource among other things it created test stubs in the `test` directory: ```bash $ bin/rails generate scaffold article title:string body:text @@ -178,14 +65,6 @@ create test/fixtures/articles.yml ... ``` -You can also generate the test stub for a model using the following command: - -```bash -$ bin/rails generate test_unit:model article title:string body:text -create test/models/article_test.rb -create test/fixtures/articles.yml -``` - The default test stub in `test/models/article_test.rb` looks like this: ```ruby @@ -250,47 +129,6 @@ An assertion is a line of code that evaluates an object (or expression) for expe Every test must contain at least one assertion, with no restriction as to how many assertions are allowed. Only when all the assertions are successful will the test pass. -### Maintaining the test database schema - -In order to run your tests, your test database will need to have the current -structure. The test helper checks whether your test database has any pending -migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` -into the test database. If migrations are still pending, an error will be -raised. Usually this indicates that your schema is not fully migrated. Running -the migrations against the development database (`bin/rake db:migrate`) will -bring the schema up to date. - -NOTE: If existing migrations required modifications, the test database needs to -be rebuilt. This can be done by executing `bin/rake db:test:prepare`. - -### Running Tests - -Running a test is as simple as invoking the file containing the test cases through `rails test` command. - -```bash -$ bin/rails test test/models/article_test.rb -. - -Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s. - -1 tests, 1 assertions, 0 failures, 0 errors, 0 skips -``` - -This will run all test methods from the test case. - -You can also run a particular test method from the test case by running the test and providing the `test method name`. - -```bash -$ bin/rails test test/models/article_test.rb test_the_truth -. - -Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s. - -1 tests, 1 assertions, 0 failures, 0 errors, 0 skips -``` - -The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary. - #### Your first failing test To see how a test failure is reported, you can add a failing test to the `article_test.rb` test case. @@ -389,116 +227,407 @@ NameError: undefined local variable or method `some_undefined_variable' for # +user_<%= n %>: + username: <%= "user#{n}" %> + email: <%= "user#{n}@example.com" %> +<% end %> +``` + +#### Fixtures in Action + +Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps: + +1. Remove any existing data from the table corresponding to the fixture +2. Load the fixture data into the table +3. Dump the fixture data into a method in case you want to access it directly + +TIP: In order to remove existing data from the database, Rails tries to disable referential integrity triggers (like foreign keys and check constraints). If you are getting annoying permission errors on running tests, make sure the database user has privilege to disable these triggers in testing environment. (In PostgreSQL, only superusers can disable all triggers. Read more about PostgreSQL permissions [here](http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html)) + +#### Fixtures are Active Record objects + +Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method whose scope is local of the test case. For example: + +```ruby +# this will return the User object for the fixture named david +users(:david) + +# this will return the property for david called id +users(:david).id + +# one can also access methods available on the User class +email(david.partner.email, david.location_tonight) +``` + +To get multiple fixtures at once, you can pass in a list of fixture names. For example: + +```ruby +# this will return an array containing the fixtures david and steve +users(:david, :steve) +``` + + +Model Testing +------------- + +Model tests are used to test the various models of your application. + +For creating Rails model tests, we use the 'test/model' directory for your application. Rails provides a generator to create an model test skeleton for you. + +```bash +$ bin/rails generate test_unit:model article title:string body:text +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`. + + +Integration Testing +------------------- + +Integration tests are used to test how various parts of your application interact. They are generally used to test important work flows within your application. + +For creating Rails integration tests, we use the 'test/integration' directory for your application. Rails provides a generator to create an integration test skeleton for you. + +```bash +$ bin/rails generate integration_test user_flows + exists test/integration/ + create test/integration/user_flows_test.rb +``` + +Here's what a freshly-generated integration test looks like: + +```ruby +require 'test_helper' + +class UserFlowsTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end +``` + +Inheriting from `ActionDispatch::IntegrationTest` comes with some advantages. This makes available some additional helpers to use in your integration tests. + +### Helpers Available for Integration Tests + +In addition to the standard testing helpers, inheriting `ActionDispatch::IntegrationTest` comes with some additional helpers available when writing integration tests. Let's briefly introduce you to the three categories of helpers you get to choose from. + +For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). + +When performing requests, you will have [`ActionDispatch::Integration::RequestHelpers`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for your use. + +If you'd like to modify the session, or state of your integration test you should look for [`ActionDispatch::Integration::Session`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. + +### Implementing an integration test + +Let's add an integration test to our blog application. We'll start with a basic workflow of creating a new blog article, to verify that everything is working properly. + +We'll start by generating our integration test skeleton: + +```bash +$ bin/rails generate integration_test blog_flow +``` -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: +It should have created a test file placeholder for us, with the output of the previous command you should see: ```bash -$ bin/rails test -b test/models/article_test.rb + invoke test_unit + create test/integration/blog_flow_test.rb ``` -If we want this test to pass we can modify it to use `assert_raises` like so: +Now let's open that file and write our first assertion: ```ruby -test "should report error" do - # some_undefined_variable is not defined elsewhere in the test case - assert_raises(NameError) do - some_undefined_variable +require 'test_helper' + +class BlogFlowTest < ActionDispatch::IntegrationTest + test "can see the welcome page" do + get "/" + assert_select "h1", "Welcome#index" end end ``` -This test should now pass. - -### Available Assertions +If you remember from earlier in the "Testing Views" section we covered `assert_select` to query the resulting HTML of a request. -By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned. +When visit our root path, we should see `welcome/index.html.erb` rendered for the view. So this assertion should pass. -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. It's not required. +#### Creating articles integration -| Assertion | Purpose | -| ---------------------------------------------------------------- | ------- | -| `assert( test, [msg] )` | Ensures that `test` is true.| -| `assert_not( test, [msg] )` | Ensures that `test` is false.| -| `assert_equal( expected, actual, [msg] )` | Ensures that `expected == actual` is true.| -| `assert_not_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.| -| `assert_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is true.| -| `assert_not_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.| -| `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.| -| `assert_not_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.| -| `assert_empty( obj, [msg] )` | Ensures that `obj` is `empty?`.| -| `assert_not_empty( obj, [msg] )` | Ensures that `obj` is not `empty?`.| -| `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.| -| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.| -| `assert_includes( collection, obj, [msg] )` | Ensures that `obj` is in `collection`.| -| `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_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( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given 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.| -| `assert_not_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.| -| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.| -| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.| -| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.| -| `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. This one is weird eh?| -| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.| +How about testing our ability to create a new article in our blog and see the resulting article. -The above are a subset of assertions that minitest supports. For an exhaustive & -more up-to-date list, please check -[Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically -[`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html) +```ruby +test "can create an article" do + get "/articles/new" + assert_response :success -Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier. + post "/articles", + params: { article: { title: "can create", body: "article successfully." } } + assert_response :redirect + follow_redirect! + assert_response :success + assert_select "p", "Title:\n can create" +end +``` -NOTE: Creating your own assertions is an advanced topic that we won't cover in this tutorial. +Let's break this test down so we can understand it. -### Rails Specific Assertions +We start by calling the `:new` action on our Articles controller. This response should be successful. -Rails adds some custom assertions of its own to the `minitest` framework: +After this we make a post request to the `:create` action of our Articles controller: -| 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)` | Assert 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`.| +```ruby +post "/articles", + params: { article: { title: "can create", body: "article successfully." } } +assert_response :redirect +follow_redirect! +``` -You'll see the usage of some of these assertions in the next chapter. +The two lines following the request are to handle the redirect we setup when creating a new article. -### A Brief Note About Minitest +NOTE: Don't forget to call `follow_redirect!` if you plan to make subsequent requests after a redirect is made. -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: +Finally we can assert that our response was successful and our new article is readable on the page. -* `ActiveSupport::TestCase` -* `ActionController::TestCase` -* `ActionMailer::TestCase` -* `ActionView::TestCase` -* `ActionDispatch::IntegrationTest` -* `ActiveJob::TestCase` +#### Taking it further -Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests. +We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use-cases for our applications. -NOTE: For more information on `Minitest`, refer to [Minitest](http://docs.seattlerb.org/minitest) Functional Tests for Your Controllers ------------------------------------- @@ -530,7 +659,7 @@ Let me take you through one such test, `test_should_get_index` from the file `ar ```ruby # articles_controller_test.rb class ArticlesControllerTest < ActionController::TestCase - def test_should_get_index + test "should get index" do get :index assert_response :success assert_includes @response.body, 'Articles' @@ -572,7 +701,7 @@ NOTE: If you try running `test_should_create_article` test from `articles_contro Let us modify `test_should_create_article` test in `articles_controller_test.rb` so that all our test pass: ```ruby -def test_should_create_article +test "should create article" do assert_difference('Article.count') do post :create, params: { article: { title: 'Some title' } } end @@ -663,7 +792,7 @@ successfully creates a new Article. Let's start by adding this assertion to our `test_should_create_article` test: ```ruby -test_should_create_article do +test "should create article" do assert_difference('Article.count') do post :create, params: { article: { title: 'Some title' } } end @@ -844,33 +973,7 @@ end Testing Routes -------------- -Like everything else in your Rails application, it is recommended that you test your routes. Below are example tests for the routes of default `show` and `create` action of `Articles` controller above and it should look like: - -```ruby -class ArticleRoutesTest < ActionController::TestCase - test "should route to article" do - assert_routing '/articles/1', { controller: "articles", action: "show", id: "1" } - end - - test "should route to create article" do - assert_routing({ method: 'post', path: '/articles' }, { controller: "articles", action: "create" }) - end -end -``` - -I've added this file here `test/controllers/articles_routes_test.rb` and if we run the test we should see: - -```bash -$ bin/rails test test/controllers/articles_routes_test.rb - -# Running: - -.. - -Finished in 0.069381s, 28.8263 runs/s, 86.4790 assertions/s. - -2 runs, 6 assertions, 0 failures, 0 errors, 0 skips -``` +Like everything else in your Rails application, you can test your routes. 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). @@ -960,8 +1063,6 @@ have to use a mixin like this: ```ruby class UserHelperTest < ActionView::TestCase - include UserHelper - test "should return the user name" do # ... end @@ -971,118 +1072,6 @@ end Moreover, since the test class extends from `ActionView::TestCase`, you have access to Rails' helper methods such as `link_to` or `pluralize`. -Integration Testing -------------------- - -Integration tests are used to test how various parts of your application interact. They are generally used to test important work flows within your application. - -For creating Rails integration tests, we use the 'test/integration' directory for your application. Rails provides a generator to create an integration test skeleton for you. - -```bash -$ bin/rails generate integration_test user_flows - exists test/integration/ - create test/integration/user_flows_test.rb -``` - -Here's what a freshly-generated integration test looks like: - -```ruby -require 'test_helper' - -class UserFlowsTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end -``` - -Inheriting from `ActionDispatch::IntegrationTest` comes with some advantages. This makes available some additional helpers to use in your integration tests. - -### Helpers Available for Integration Tests - -In addition to the standard testing helpers, inheriting `ActionDispatch::IntegrationTest` comes with some additional helpers available when writing integration tests. Let's briefly introduce you to the three categories of helpers you get to choose from. - -For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). - -When performing requests, you will have [`ActionDispatch::Integration::RequestHelpers`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for your use. - -If you'd like to modify the session, or state of your integration test you should look for [`ActionDispatch::Integration::Session`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. - -### Implementing an integration test - -Let's add an integration test to our blog application. We'll start with a basic workflow of creating a new blog article, to verify that everything is working properly. - -We'll start by generating our integration test skeleton: - -```bash -$ bin/rails generate integration_test blog_flow -``` - -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/integration/blog_flow_test.rb -``` - -Now let's open that file and write our first assertion: - -```ruby -require 'test_helper' - -class BlogFlowTest < ActionDispatch::IntegrationTest - test "can see the welcome page" do - get "/" - assert_select "h1", "Welcome#index" - end -end -``` - -If you remember from earlier in the "Testing Views" section we covered `assert_select` to query the resulting HTML of a request. - -When visit our root path, we should see `welcome/index.html.erb` rendered for the view. So this assertion should pass. - -#### Creating articles integration - -How about testing our ability to create a new article in our blog and see the resulting article. - -```ruby -test "can create an article" do - get "/articles/new" - assert_response :success - - post "/articles", - params: { article: { title: "can create", body: "article successfully." } } - assert_response :redirect - follow_redirect! - assert_response :success - assert_select "p", "Title:\n can create" -end -``` - -Let's break this test down so we can understand it. - -We start by calling the `:new` action on our Articles controller. This response should be successful. - -After this we make a post request to the `:create` action of our Articles controller: - -```ruby -post "/articles", - params: { article: { title: "can create", body: "article successfully." } } -assert_response :redirect -follow_redirect! -``` - -The two lines following the request are to handle the redirect we setup when creating a new article. - -NOTE: Don't forget to call `follow_redirect!` if you plan to make subsequent requests after a redirect is made. - -Finally we can assert that our response was successful and our new article is readable on the page. - -#### Taking it further - -We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use-cases for our applications. - Testing Your Mailers -------------------- @@ -1235,16 +1224,3 @@ class ProductTest < ActiveJob::TestCase end end ``` - -Other Testing Approaches ------------------------- - -The built-in `minitest` based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including: - -* [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use. -* [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures. -* [Fixture Builder](https://github.com/rdy/fixture_builder), a tool that compiles Ruby factories into fixtures before a test run. -* [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests. -* [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions. -* [RSpec](http://relishapp.com/rspec), a behavior-driven development framework -* [Capybara](http://jnicklas.github.com/capybara/), Acceptance test framework for web applications -- cgit v1.2.3