Question

I've done a bit of googling on the topic, and I'm still confused.

I'm building a custom help page with the Zendesk API Ruby Client, and I'm at a stage when I need to test the creation of the ZendeskAPI::Ticket resource. The following code is in the spec/features directory. It fills out a form with valid values and submits the form to the #create action. Fairly standard, simple stuff.

require 'spec_helper'

feature 'ticket creation' do
  scenario 'user creates new ticket' do
    visit root_path

    fill_in 'Name', with: 'Billybob Joe'
    fill_in 'Email', with: 'joe@test.com'
    fill_in 'Subject', with: 'Aw, goshdarnit!'
    fill_in 'Issue', with: 'My computer spontaneously blew up!'
    click_button 'Create Ticket'

    expect(page).to have_content('Ticket details')
  end
end

And here is the relevant part of the Tickets controller. The ticket_valid? method supplies minimal validations for the options hash and client is an instance of the ZendeskAPI::Client.

def create
  options = { subject: params[:subject], comment: { value: params[:issue] }, 
              requester: params[:email], priority: 'normal' }
  if ticket_valid? options
    flash[:success] = 'Ticket created.'
    @ticket = ZendeskAPI::Ticket.create(client, options)
    redirect_to ticket_path(@ticket.id)
  else
    flash[:error] = 'Something went wrong. Try again.'
    redirect_to root_url
  end
end

Problem is, whenever I run the tests, an actual ticket is created in the Zendesk backend that I'll have to delete manually later when I just want to test for successful form submission without actually creating a ticket.

So my question is, how can I test the ticket creation form without creating an actual ticket in the Zendesk backend whenever I run the tests?

The articles and blogs I've been reading as a result of my googling vaguely refers to using RackTest, while others suggest not using Capybara at all for this sort of thing, which leaves me even more confused. I'm still relatively new to RSpec and even newer to dealing with building Rails apps with an API, so a clear explanation would be great.

Thanks in advance!! You're awesome.

Was it helpful?

Solution 2

If you don't want to call the Zendesk, you'll have to create a "test double" instead of the actual call. The test double capability that comes with RSpec is described minimally at https://github.com/rspec/rspec-mocks, but is covered more comprehensively in blogs and books.

The answer posted simultaneously to this discusses creating a separate class, but still seems to involve creating a Zendesk ticket. You don't actually need a separate class and you don't need to create any ZendeskAPI objects at all. Instead, you would stub ZendeskAPI::Ticket#create to return a test double which in turn would need to serve up whatever Zendesk ticket methods the rest of your test needs, which at least includes id.

The use of Capybara is really a secondary issue and refers to how you drive the test. But note that your test currently requires rendering the ticket page and checking the content of that page. If you want to test "just" your controller, then you can/should just test that it makes the proper calls (e.g. to ZendeskAPI::Ticket) and redirects to the appropriate page. Further, if you do just that, you have much less to simulate in your test double.

OTHER TIPS

One way to do this would be abstract away your interface to ZenDesk into your own class and then mock it in your tests.

For example, you could create an interface class:

class ZendeskGateway

  def create_ticket(client, options)
    ZendeskAPI::Ticket.create(client, options)
  end

end

Then, in your code, you replace the usage of the Zendesk API in your controller with your interface class:

class TicketsController < ApplicationController

  attr_accessor :zendesk_gateway

  after_initialize :init

  def init
    @zendesk_gateway = ZendeskGateway.new
  end 

  def create
    options = { subject: params[:subject], comment: { value: params[:issue] }, 
                  requester: params[:email], priority: 'normal' }
    if ticket_valid? options
      flash[:success] = 'Ticket created.'

      @ticket = @zendesk_gateway.create_ticket(client, options)

      redirect_to ticket_path(@ticket.id)
    else
      flash[:error] = 'Something went wrong. Try again.'
      redirect_to root_url
    end

  end

end

Now that it is abstracted, you can using a mocking framework (like mocha) to stub out the method during your test so that it doesn't actually call out to Zendesk:

zendesk_ticket = ZendeskAPI::Ticket.new(client, :id => 1, :priority => "urgent")

@controller.zendesk_gateway.expects(:create_ticket).returns(zendesk_ticket)

This was a very quick/dirty example. But hopefully you can see the general idea.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top