Basic TDD in Rails : Testing HTTP Basic Authentication

Objective


To add HTTP Basic Authentication for Create, Edit and Delete actions.

Steps


Step 1

Change the spec for 'creating a new article' in articles_controller_spec.rb:

  it 'should not create a new article when admin is not logged in', :focus => true do
    expect do
      post :create, { article: { title: 'test', description: 'test' }}
    end.to_not change(Article, :count)
  end

Step 2

Run the test.

$rspec spec/controllers/articles_controller_spec.rb --tag focus

The test fails.

 1) ArticlesController should create a new article
     Failure/Error: expect do
       expected #count not to have changed, but did change from 0 to 1

Step 3

Add the http_basic authentication in articles controller:

http_basic_authenticate_with name: 'user', password: 'secret', only: [:create]

Step 4

Run the test.

$rspec spec/controllers/articles_controller_spec.rb --tag focus

It passes.

Step 5

Run all the tests:

$rspec spec/controllers/articles_controller_spec.rb

All the tests related to create fails. Using focus tag helps us to resolve one test at a time. Now we have the tests running for the create. Let's mark the failing tests as pending.

  xit 'should redirect to articles index page after creating the article' do
  xit 'should redirect to articles index page after creating the article' do

Step 6

Run all the tests.

Step 7

Add a new test:

  it 'should create a new article when admin is logged in', :focus => true do
    expect do
      post :create, { article: { title: 'test', description: 'test' }}
    end.to change(Article, :count)
  end

It fails.

Step 8

Create spec/support/controller_helpers.rb:

module ControllerHelpers
  def http_basic_auth
    request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('user','secret')
  end  
end

Step 9

Uncomment:

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

line in rails_helper.rb.

Step 10

Add configuration for our controller_helpers.rb in rails_helper.rb.

RSpec.configure do |config|  
  config.include ControllerHelpers, :type => :controller

end

Step 11

Run the test. The new test passes.

Step 12

Let's create a context block for the case when the admin is logged in and include all the relevant tests inside that block as follows:

  context 'Admin Logged In' do
    before(:each) do
      http_basic_auth  
    end

    it 'should create a new article when admin is logged in' do
      expect do
        post :create, { article: { title: 'test', description: 'test' }}
      end.to change(Article, :count)
    end

    it 'should redirect to articles index page after creating the article' do
      post :create, { article: { title: 'test', description: 'test' }}

      expect(response).to redirect_to articles_path
    end

    it 'should render the new article page if the validation fails' do
      post :create, { article: { title: 'test' }}

      expect(response).to render_template(:new)
    end
  end

I have included the commented out tests inside the new context block. All tests pass now.

Step 13

We can create another context block for the case when admin is not logged in:

context 'Admin not logged in' do
  it 'should not create a new article when admin is not logged in' do
    expect do
      post :create, { article: { title: 'test', description: 'test' }}
    end.to_not change(Article, :count)
  end

end

Step 14

All tests pass.

Step 15

Write the next test:

context 'Admin not logged in' do
  it 'should not display the edit article page' do
    article = Article.create(title: 'test', description: 'test')
    get :edit, { id: article.id }

    expect(response.code).to eq('401')
  end

end

Step 16

Run the test. It fails with:

 1) ArticlesController Admin not logged in should not display the edit article page
     Failure/Error: expect(response.code).to eq(401)

       expected: "401"
            got: "200"

Step 17

http_basic_authenticate_with name: 'user', password: 'secret', only: [:create, :edit]

Step 18

Run the test.

$rspec spec/controllers/articles_controller_spec.rb

It passes.

Step 19

For admin logged in, add the test:

it 'should render edit page' do
  article = Article.create(title: 'test', description: 'test')
  get :edit, { id: article.id }

  expect(response.code).to eq('200')
end

It passes.

Step 20

Protect the update action. Look at the output of rake routes. We will use the http verb for update action in our test.

it 'should return unauthorized status code for update article' do
  article = Article.create(title: 'test', description: 'test')
  put :update, { id: article.id, article: { title: 'updated test' }}

  expect(response.code).to eq('401')
end

Step 21

Run the test. It fails.

1) ArticlesController Admin not logged in should return unauthorized status code for update article
     Failure/Error: expect(response.code).to eq('401')

       expected: "401"
            got: "302"

Step 22

http_basic_authenticate_with name: 'user', password: 'secret', only: [:create, :edit, :update]

Step 23

Run the test.

$rspec spec/controllers/articles_controller_spec.rb

It passes.

Step 24

For admin logged in:

it 'should update the article' do
  article = Article.create(title: 'test', description: 'test')
  put :update, { id: article.id, article: { title: 'updated test' }}

  article = Article.last

  expect(article.title).to eq('updated test')
end

The test passes without failing. That's fine.

Exercise


Write a test for deleting an article when the admin is logged in. Write the test for both when the admin is logged in and when admin is not logged in.

Answer


Admin logged in:

it 'should delete the article' do
  article = Article.create(title: 'test', description: 'test')
  delete :destroy, { id: article.id }

  expect(Article.count).to eq(0)  
end

Admin not logged in:

it 'should not delete the article' do
  article = Article.create(title: 'test', description: 'test')
  delete :destroy, { id: article.id }

  expect(Article.count).to eq(1)  
end

You get the error:

 1) ArticlesController Admin not logged in should not delete the article
     Failure/Error: expect(Article.count).to eq(1)

       expected: 1
            got: 0

Change the controller:

http_basic_authenticate_with name: 'user', password: 'secret', only: [:create, :edit, :update, :destroy]

Run the test. All the tests will now pass.

Summary


In this lesson you learned how to write controller tests for HTTP Basic authentication. You also learned how to create reusable helpers that can be used in your controller tests.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.