Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs

Dashboard
Notifications
Mark all as read
Q&A

Post History

#3: Post edited by user avatar ArtOfCode‭ · 2021-01-21T01:48:50Z (11 months ago)
#2: Post edited by user avatar ArtOfCode‭ · 2021-01-15T23:50:45Z (11 months ago)
  • We have some limited automated testing already, particularly around core features. This guide covers how to create new tests.
  • ## Who can write tests?
  • Anyone who's comfortable with a little bit of relatively simple code.
  • ## What do I need?
  • You need _at least_:
  • * the repository cloned to your local machine
  • * a way of editing the code (you _can_ use Notepad or similar, but an IDE like VS Code or Atom works much better)
  • Ideally, you'd also have:
  • * a full development environment (i.e. you're able to run the application locally; see [this guide](https://collab.codidact.org/posts/280451) on how to set that up)
  • * comfortable running commands in your OS' terminal
  • You don't need significant development experience.
  • ## What can we test?
  • Our testing system simulates sending a request to the application, and then using the response to assert that various conditions are met, depending on what you want to test. This means that you _can_ test:
  • * whether the request succeeds (HTTP 200) or errors (HTTP 5xx class errors)
  • * whether the correct changes are applied in the database
  • * whether the format (HTML/JSON/etc) and content (messages, statuses, errors, etc) of the response is correct
  • You _can't_ test:
  • * the UI, or whether it sends the request in the correct format
  • * most outside integrations, like SE OAuth or 3rd-party login (which _can_ be tested, but often require more complex procedures or additional support to be set up — poke Art or luap42 instead)
  • ## How do I write a test?
  • Tests are held in the `test/` directory of the repository. Most of our tests are _controller_ tests, which live in `test/controllers/` and do as described above - simulate a request, test the response.
  • The following set of steps is an exhaustive guide. If you vaguely know what you're doing, or like to learn it yourself by playing around with it, here's an example test that you can take and mess with:
  • ```ruby
  • test 'post edit should save and create history' do
  • sign_in users(:standard_user)
  • post = posts(:question_one)
  • before_history = PostHistory.where(post_id: post.id).count
  • post :update, params: { id: post.id, post: { title: 'test new post title' } }
  • assert_response 200
  • assert_not_nil assigns(:post)
  • assert_equal 'test new post title', assigns(:post).title
  • after_history = PostHistory.where(post_id: post.id).count
  • assert_equal before_history + 1, after_history
  • end
  • ```
  • 1. **Identify the controller and action**
  • Once you know what you want to test, identify the controller and action that it lives under. Most of the time you can do this from the URL — a pathname of `/posts/new` (for example) is likely to be the `new` action of the `posts` controller — or, as Rails expresses it, `posts#new`. If you're not sure, you can try to find the path in `config/routes.rb`, or run `rails routes` in a console to dump a list of all paths, and find the path in there. If you can't find it, poke a developer.
  • 2. **Write a skeleton test**
  • Controller tests follow this format:
  • ```ruby
  • test 'give your test a descriptive name' do
  • # sign-in & prepare
  • # request
  • # collection
  • # test response
  • end
  • ```
  • Not all tests need all of those stages. Add a skeleton test as above into the relevant file (i.e. if you're testing the `comments` controller, your test goes in `test/controllers/comments_controller_test.rb`), and give it a name that describes what the test is _expecting_.
  • 3. **Prepare & request**
  • The first part of the test is any preparation, including sign-in or collecting any values that will be changed by the request that you want to test for. Let's say we're testing that when a user edits a post, the changes are saved and a PostHistory event is created. This needs:
  • * a user to be signed in
  • * a count of the history events _before_ the edit, so we can check the count after is equal to one more than this
  • You can sign a user in by using the `sign_in` helper. You must pass to it the user that you want to be signed in - there's a list of these in `test/fixtures/users.yml` (see "How do I use fixtures?", below, for more information). To sign in a standard user, add this line to your test:
  • ```ruby
  • sign_in users(:standard_user)
  • ```
  • Pick the post you're going to test. Let's use `:question_one`, because `:standard_user` is the author for it so doesn't need permissions to edit it. Again, there's a list of posts in `test/fixtures/posts.yml`, and you can get the post instance with `posts(:question_one)`. Add this line to your test:
  • ```ruby
  • post = posts(:question_one)
  • ```
  • To get the history count, you need to select from the `post_history` table any events where the post ID matches the one we're testing, and count them. Add this line to your test:
  • ```ruby
  • before_history = PostHistory.where(post_id: post.id).count
  • ```
  • 4. **Run the request**
  • Now you need to actually simulate the request. There are methods for each HTTP method — so if you need a GET request, you'll run `get`, and if you need a POST request it'll be `post`. You must pass to it the name of the action (which you identified earlier) that you're testing, and any parameters that you want to submit in the request. In this case, we'll just edit the title. The `update` action (which handles edits) expects a POST request with a post `id`, and a `post` object containing any updated details. So, our request will look like this — add this to your test:
  • ```ruby
  • post :update, params: { id: post.id, post: { title: 'test new edited title' } }
  • ```
  • 5. **Collect results**
  • Before you make any assertions, collect any data or results you need to use. In this case, we need to know the number of post history events for this post _after_ we've made the request, which should be one more than before it. Same code as before for this, but in a different variable:
  • ```ruby
  • after_history = PostHistory.where(id: post.id).count
  • ```
  • 6. **Test!**
  • To test, you use `assert` methods. The most simple is `assert`, which passes if the data or condition you pass to it is `true`, and fails otherwise. There are plenty of others, but we'll use the most common here. You should use the most specific `assert` method for what you're trying to test, as that makes the error message more specific when it fails.
  • For this test, we'll test that:
  • * the response is a success (HTTP 200)
  • * the server assigns a `@post` variable for the post instance
  • * the `@post` has been updated with our new title
  • * there is 1 more history event than there was before the request
  • Add these lines to your test:
  • ```ruby
  • assert_response 200
  • assert_not_nil assigns(:post)
  • assert_equal 'test new edited title', assigns(:post).title
  • assert_equal before_history + 1, after_history
  • ```
  • 7. **Run your test**
  • You can run the full test suite by running `rails test` (or `rails t`, for shorthand) in a terminal. Running the full suite will take a while, though (10 minutes or more), so you can also pass in the path to the file containing your test, and Rails will run _just_ the tests in that file. In this case, run:
  • ```bash
  • rails test test/controllers/posts_controller_test.rb
  • ```
  • 8. **PR**
  • Now submit a pull request on GitHub to add your changes - either push the changes to your own fork if you don't have push access and pull request from there, or push to a branch and open the PR from there. If you can get all the tests to pass including your new tests, great! If you're having trouble, you can always submit the PR anyway and developers will be able to help you get the test right.
  • We have some limited automated testing already, particularly around core features. This guide covers how to create new tests.
  • ## Who can write tests?
  • Anyone who's comfortable with a little bit of relatively simple code.
  • ## What do I need?
  • You need _at least_:
  • * the repository cloned to your local machine
  • * a way of editing the code (you _can_ use Notepad or similar, but an IDE like VS Code or Atom works much better)
  • Ideally, you'd also have:
  • * a full development environment (i.e. you're able to run the application locally; see [this guide](https://collab.codidact.org/posts/280451) on how to set that up)
  • * comfortable running commands in your OS' terminal
  • You don't need significant development experience.
  • ## What can we test?
  • Our testing system simulates sending a request to the application, and then using the response to assert that various conditions are met, depending on what you want to test. This means that you _can_ test:
  • * whether the request succeeds (HTTP 200) or errors (HTTP 5xx class errors)
  • * whether the correct changes are applied in the database
  • * whether the format (HTML/JSON/etc) and content (messages, statuses, errors, etc) of the response is correct
  • You _can't_ test:
  • * the UI, or whether it sends the request in the correct format
  • * most outside integrations, like SE OAuth or 3rd-party login (which _can_ be tested, but often require more complex procedures or additional support to be set up — poke Art or luap42 instead)
  • ## How do I write a test?
  • Tests are held in the `test/` directory of the repository. Most of our tests are _controller_ tests, which live in `test/controllers/` and do as described above - simulate a request, test the response.
  • The following set of steps is an exhaustive guide. If you vaguely know what you're doing, or like to learn it yourself by playing around with it, here's an example test that you can take and mess with:
  • ```ruby
  • test 'post edit should save and create history' do
  • sign_in users(:standard_user)
  • post = posts(:question_one)
  • before_history = PostHistory.where(post_id: post.id).count
  • post :update, params: { id: post.id, post: { title: 'test new post title' } }
  • assert_response 200
  • assert_not_nil assigns(:post)
  • assert_equal 'test new post title', assigns(:post).title
  • after_history = PostHistory.where(post_id: post.id).count
  • assert_equal before_history + 1, after_history
  • end
  • ```
  • 1. **Identify the controller and action**
  • Once you know what you want to test, identify the controller and action that it lives under. Most of the time you can do this from the URL — a pathname of `/posts/new` (for example) is likely to be the `new` action of the `posts` controller — or, as Rails expresses it, `posts#new`. If you're not sure, you can try to find the path in `config/routes.rb`, or run `rails routes` in a console to dump a list of all paths, and find the path in there. If you can't find it, poke a developer.
  • 2. **Write a skeleton test**
  • Controller tests follow this format:
  • ```ruby
  • test 'give your test a descriptive name' do
  • # sign-in & prepare
  • # request
  • # collection
  • # test response
  • end
  • ```
  • Not all tests need all of those stages. Add a skeleton test as above into the relevant file (i.e. if you're testing the `comments` controller, your test goes in `test/controllers/comments_controller_test.rb`), and give it a name that describes what the test is _expecting_.
  • 3. **Prepare & request**
  • The first part of the test is any preparation, including sign-in or collecting any values that will be changed by the request that you want to test for. Let's say we're testing that when a user edits a post, the changes are saved and a PostHistory event is created. This needs:
  • * a user to be signed in
  • * a count of the history events _before_ the edit, so we can check the count after is equal to one more than this
  • You can sign a user in by using the `sign_in` helper. You must pass to it the user that you want to be signed in - there's a list of these in `test/fixtures/users.yml` (see "How do I use fixtures?", below, for more information). To sign in a standard user, add this line to your test:
  • ```ruby
  • sign_in users(:standard_user)
  • ```
  • Pick the post you're going to test. Let's use `:question_one`, because `:standard_user` is the author for it so doesn't need permissions to edit it. Again, there's a list of posts in `test/fixtures/posts.yml`, and you can get the post instance with `posts(:question_one)`. Add this line to your test:
  • ```ruby
  • post = posts(:question_one)
  • ```
  • To get the history count, you need to select from the `post_history` table any events where the post ID matches the one we're testing, and count them. Add this line to your test:
  • ```ruby
  • before_history = PostHistory.where(post_id: post.id).count
  • ```
  • 4. **Run the request**
  • Now you need to actually simulate the request. There are methods for each HTTP method — so if you need a GET request, you'll run `get`, and if you need a POST request it'll be `post`. You must pass to it the name of the action (which you identified earlier) that you're testing, and any parameters that you want to submit in the request. In this case, we'll just edit the title. The `update` action (which handles edits) expects a POST request with a post `id`, and a `post` object containing any updated details. So, our request will look like this — add this to your test:
  • ```ruby
  • post :update, params: { id: post.id, post: { title: 'test new edited title' } }
  • ```
  • 5. **Collect results**
  • Before you make any assertions, collect any data or results you need to use. In this case, we need to know the number of post history events for this post _after_ we've made the request, which should be one more than before it. Same code as before for this, but in a different variable:
  • ```ruby
  • after_history = PostHistory.where(id: post.id).count
  • ```
  • 6. **Test!**
  • To test, you use `assert` methods. The most simple is `assert`, which passes if the data or condition you pass to it is `true`, and fails otherwise. There are plenty of others, but we'll use the most common here. You should use the most specific `assert` method for what you're trying to test, as that makes the error message more specific when it fails.
  • For this test, we'll test that:
  • * the response is a success (HTTP 200)
  • * the server assigns a `@post` variable for the post instance
  • * the `@post` has been updated with our new title
  • * there is 1 more history event than there was before the request
  • Add these lines to your test:
  • ```ruby
  • assert_response 200
  • assert_not_nil assigns(:post)
  • assert_equal 'test new edited title', assigns(:post).title
  • assert_equal before_history + 1, after_history
  • ```
  • 7. **Run your test**
  • You can run the full test suite by running `rails test` (or `rails t`, for shorthand) in a terminal. Running the full suite will take a while, though (10 minutes or more), so you can also pass in the path to the file containing your test, and Rails will run _just_ the tests in that file. In this case, run:
  • ```bash
  • rails test test/controllers/posts_controller_test.rb
  • ```
  • 8. **PR**
  • Now submit a pull request on GitHub to add your changes - either push the changes to your own fork if you don't have push access and pull request from there, or push to a branch and open the PR from there. If you can get all the tests to pass including your new tests, great! If you're having trouble, you can always submit the PR anyway and developers will be able to help you get the test right.
  • ---
  • The above is all you need to successfully write a test case. If you want more detailed information, read on.
  • ## How do I use fixtures?
  • _Fixtures_ are pre-set-up data for each table we have in the database, so that you have some data to test on. The steps above used a couple of fixtures in `users(:standard_user)` and `posts(:question_one)`.
  • Fixtures are held under `test/fixtures/`. Each data type has its own file, and each file contains multiple fixtures for that type. For instance, for users, we've already defined standard users, editors (`edit_posts` ability), deleters (`flag_curate` ability), moderators, admins, and global mods and admins. For posts, we have a mix of questions, answers, and articles, in various states.
  • If you're looking to test on a particular condition, have a look at the fixtures to see if what you need is there. For instance, you might be looking to test that a standard user can't post in a high-trust-level category - for that, you'd need the standard user and the high-trust-level category defined in fixtures.
  • Fixture files are written in [YAML](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html). Each fixture consists of an identifying name (like `standard_user`), then the attributes for that fixture, which correspond to the database fields. For instance, this is an example definition for a user:
  • ```yaml
  • test_user:
  • username: standard-user
  • email: [email protected]
  • profile_markdown: ~
  • ```
  • You'd access that user in test cases with `users(:test_user)`. Same for any other fixture: `categories(:test_category)` or `audit_logs(:random_name_here)` also work. If the `random_name_here` AuditLog isn't defined, it'll just return `nil` for that fixture, so make sure the fixture you're referencing is actually defined.
  • ## More help/documentation
  • * [Available assertions](https://guides.rubyonrails.org/v5.2/testing.html#available-assertions) including [Rails-specific assertions](https://guides.rubyonrails.org/v5.2/testing.html#rails-specific-assertions)
  • * [YAML syntax](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html)
  • * [Full guide to Rails testing](https://guides.rubyonrails.org/v5.2/testing.html)
#1: Initial revision by user avatar ArtOfCode‭ · 2021-01-15T18:49:55Z (11 months ago)
How do I write a new test case?
We have some limited automated testing already, particularly around core features. This guide covers how to create new tests.

## Who can write tests?
Anyone who's comfortable with a little bit of relatively simple code.

## What do I need?
You need _at least_:
 * the repository cloned to your local machine
 * a way of editing the code (you _can_ use Notepad or similar, but an IDE like VS Code or Atom works much better)

Ideally, you'd also have:
 * a full development environment (i.e. you're able to run the application locally; see [this guide](https://collab.codidact.org/posts/280451) on how to set that up)
 * comfortable running commands in your OS' terminal

You don't need significant development experience.

## What can we test?
Our testing system simulates sending a request to the application, and then using the response to assert that various conditions are met, depending on what you want to test. This means that you _can_ test:

 * whether the request succeeds (HTTP 200) or errors (HTTP 5xx class errors)
 * whether the correct changes are applied in the database
 * whether the format (HTML/JSON/etc) and content (messages, statuses, errors, etc) of the response is correct

You _can't_ test:

 * the UI, or whether it sends the request in the correct format
 * most outside integrations, like SE OAuth or 3rd-party login (which _can_ be tested, but often require more complex procedures or additional support to be set up — poke Art or luap42 instead)

## How do I write a test?
Tests are held in the `test/` directory of the repository. Most of our tests are _controller_ tests, which live in `test/controllers/` and do as described above - simulate a request, test the response.

The following set of steps is an exhaustive guide. If you vaguely know what you're doing, or like to learn it yourself by playing around with it, here's an example test that you can take and mess with:

```ruby
test 'post edit should save and create history' do
  sign_in users(:standard_user)
  post = posts(:question_one)
  before_history = PostHistory.where(post_id: post.id).count
  post :update, params: { id: post.id, post: { title: 'test new post title' } }
  assert_response 200
  assert_not_nil assigns(:post)
  assert_equal 'test new post title', assigns(:post).title
  after_history = PostHistory.where(post_id: post.id).count
  assert_equal before_history + 1, after_history
end
```

1. **Identify the controller and action**  
   Once you know what you want to test, identify the controller and action that it lives under. Most of the time you can do this from the URL — a pathname of `/posts/new` (for example) is likely to be the `new` action of the `posts` controller — or, as Rails expresses it, `posts#new`. If you're not sure, you can try to find the path in `config/routes.rb`, or run `rails routes` in a console to dump a list of all paths, and find the path in there. If you can't find it, poke a developer.

2. **Write a skeleton test**  
   Controller tests follow this format:

   ```ruby
   test 'give your test a descriptive name' do
     # sign-in & prepare
     # request
     # collection
     # test response
   end
   ```

   Not all tests need all of those stages. Add a skeleton test as above into the relevant file (i.e. if you're testing the `comments` controller, your test goes in `test/controllers/comments_controller_test.rb`), and give it a name that describes what the test is _expecting_.

3. **Prepare & request**  
   The first part of the test is any preparation, including sign-in or collecting any values that will be changed by the request that you want to test for. Let's say we're testing that when a user edits a post, the changes are saved and a PostHistory event is created. This needs:

    * a user to be signed in
    * a count of the history events _before_ the edit, so we can check the count after is equal to one more than this

   You can sign a user in by using the `sign_in` helper. You must pass to it the user that you want to be signed in - there's a list of these in `test/fixtures/users.yml` (see "How do I use fixtures?", below, for more information). To sign in a standard user, add this line to your test:

   ```ruby
   sign_in users(:standard_user)
   ```

   Pick the post you're going to test. Let's use `:question_one`, because `:standard_user` is the author for it so doesn't need permissions to edit it. Again, there's a list of posts in `test/fixtures/posts.yml`, and you can get the post instance with `posts(:question_one)`. Add this line to your test:

   ```ruby
   post = posts(:question_one)
   ```

   To get the history count, you need to select from the `post_history` table any events where the post ID matches the one we're testing, and count them. Add this line to your test: 

   ```ruby
   before_history = PostHistory.where(post_id: post.id).count
   ```

4. **Run the request**  
   Now you need to actually simulate the request. There are methods for each HTTP method — so if you need a GET request, you'll run `get`, and if you need a POST request it'll be `post`. You must pass to it the name of the action (which you identified earlier) that you're testing, and any parameters that you want to submit in the request. In this case, we'll just edit the title. The `update` action (which handles edits) expects a POST request with a post `id`, and a `post` object containing any updated details. So, our request will look like this — add this to your test:

   ```ruby
   post :update, params: { id: post.id, post: { title: 'test new edited title' } }
   ```

5. **Collect results**  
   Before you make any assertions, collect any data or results you need to use. In this case, we need to know the number of post history events for this post _after_ we've made the request, which should be one more than before it. Same code as before for this, but in a different variable:

   ```ruby
   after_history = PostHistory.where(id: post.id).count
   ```

6. **Test!**  
   To test, you use `assert` methods. The most simple is `assert`, which passes if the data or condition you pass to it is `true`, and fails otherwise. There are plenty of others, but we'll use the most common here. You should use the most specific `assert` method for what you're trying to test, as that makes the error message more specific when it fails.

   For this test, we'll test that:

    * the response is a success (HTTP 200)
    * the server assigns a `@post` variable for the post instance
    * the `@post` has been updated with our new title
    * there is 1 more history event than there was before the request

   Add these lines to your test:

   ```ruby
   assert_response 200
   assert_not_nil assigns(:post)
   assert_equal 'test new edited title', assigns(:post).title
   assert_equal before_history + 1, after_history
   ```

7. **Run your test**  
   You can run the full test suite by running `rails test` (or `rails t`, for shorthand) in a terminal. Running the full suite will take a while, though (10 minutes or more), so you can also pass in the path to the file containing your test, and Rails will run _just_ the tests in that file. In this case, run:

   ```bash
   rails test test/controllers/posts_controller_test.rb
   ```

8. **PR**  
   Now submit a pull request on GitHub to add your changes - either push the changes to your own fork if you don't have push access and pull request from there, or push to a branch and open the PR from there. If you can get all the tests to pass including your new tests, great! If you're having trouble, you can always submit the PR anyway and developers will be able to help you get the test right.