Как выполнить тестирование JSON-контроллера?

Это мое действие:

def my_action
  str = ... # get json str somehow  
  render :json => str
end

Это мой тест:

test "my test" do 
  post(:my_action, {'param' => "value"}    
  assert_response :success
end

Я хочу добавить еще одно утверждение о том, что излучаемый JSON содержит некоторое значение. Как я могу это сделать в тестовом модуле контроллера, а не путем анализа результата просмотра?

Ответ 1

Как и люди, о которых говорилось выше, это будет функциональный тест.

Лучший способ - это, вероятно, сделать запрос, проанализировать тело ответа JSON и сопоставить его с ожидаемым результатом.

Если у меня есть companies_controller в Rspec с помощью FactoryGirl:

describe "GET 'show'" do

  before(:each) do
    @company = Factory(:company)
    get 'show', :format => :json, :id => @company.id
  end

  it "should be successful" do
     response.should be_success
  end

  it "should return the correct company when correct id is passed" do
    body = JSON.parse(response.body)
    body["id"].should == @company.id
  end

end

Вы можете проверить другие атрибуты одинаково. Кроме того, у меня обычно есть контекст invalid, где я пытаюсь передать недопустимые параметры.

Ответ 2

Использование встроенного функционального теста Rails:

require 'test_helper'

class ZombiesControllerTest < ActionController::TestCase

  setup do
    @request.headers['Accept'] = Mime::JSON
    @request.headers['Content-Type'] = Mime::JSON.to_s

  end

  test "should post my action" do
    post :my_action, { 'param' => "value" }, :format => "json"
    assert_response :success
    body = JSON.parse(response.body)
    assert_equal "Some returned value", body["str"]
  end

end

Ответ 3

Мой подход к этому немного отличается, если я использую Jbuilder gem, который теперь доступен из команды Rails. (Этот подход применим к другим драгоценным камням, которые делают JSON или XML как представления.) Я предпочитаю модульные тесты по функциональным испытаниям, когда это возможно, поскольку они могут быть довольно быстрыми. С Jbuilder вы можете преобразовать большинство тестов в модульные тесты.

Да, у вас все еще есть функциональные тесты на контроллере, но их очень мало, и они не анализируют JSON. Функциональный тест проверяет только логику контроллера, а не обработанную JSON. Функциональный тест для действительного запроса может утверждать следующее (RSpec):

  assert_response :success
  expect(response).to render_template(:show)
  expect(assigns(:item).id).to eq(expected_item.id)

Я просто проверяю, что он успешный, он отображает шаблон и передает элемент в шаблон. На этом этапе представление имеет информацию, необходимую для правильного рендеринга.

Теперь протестируйте JSON, отображаемый модулем, проверяющим представление Jbuilder.

describe 'api/v1/items/show.json.jbuilder' do
  it 'includes foo' do
    assign(:item, account.build(foo: 'bar'))

    render

    json = JSON.parse(rendered)
    expect(json['item']['foo']).to eq('bar')
  end

  # A bunch of other JSON tests...

Ответ 4

Этот тест контроллера хорошо работал у меня с помощью Minitest с Rails 4.2.4:

require 'test_helper'

class ThingsControllerTest < ActionController::TestCase

  test "should successfully create a new thing" do
    assert_difference 'Thing.count' do
      @request.headers["Accept"] = "application/json"

      post(:create, {thing: {name: "My Thing"}})
    end

    assert_response :success

    json_response = JSON.parse(@response.body)
    assert_equal json_response["name"], "My Thing"
  end

end

И это работало в форме теста интеграции.

require 'test_helper'

class ThingsRequestsTest < ActionDispatch::IntegrationTest

  test "creates new thing" do
    assert_difference 'Thing.count' do
      post("/things.json", {thing: { name: "My Thing"}})
    end

    assert_equal 201, status

    json_response = JSON.parse(@response.body)
    assert_equal json_response["name"], "My Thing"
  end

end

Честно говоря, странно пытаться сохранить синтаксические нюансы прямо от одного типа теста к другому.