我的控制器中有以下代码:
format.json { render :json => {
:flashcard => @flashcard,
:lesson => @lesson,
:success => true
}
在我的RSpec控制器测试中,我想验证某个场景确实收到了成功的json响应,所以我有以下一行:
controller.should_receive(:render).with(hash_including(:success => true))
虽然当我运行测试时出现以下错误:
Failure/Error: controller.should_receive(:render).with(hash_including(:success => false))
(#<AnnoController:0x00000002de0560>).render(hash_including(:success=>false))
expected: 1 time
received: 0 times
我是否错误地检查了回复?
答案 0 :(得分:160)
您可以检查响应对象并验证它是否包含预期值:
@expected = {
:flashcard => @flashcard,
:lesson => @lesson,
:success => true
}.to_json
get :action # replace with action name / params as necessary
response.body.should == @expected
修改强>
将其更改为post
会让它变得有点棘手。这是处理它的方法:
it "responds with JSON" do
my_model = stub_model(MyModel,:save=>true)
MyModel.stub(:new).with({'these' => 'params'}) { my_model }
post :create, :my_model => {'these' => 'params'}, :format => :json
response.body.should == my_model.to_json
end
请注意,mock_model
不会回复to_json
,因此需要stub_model
或真实模型实例。
答案 1 :(得分:152)
您可以像这样解析响应主体:
parsed_body = JSON.parse(response.body)
然后,您可以对已解析的内容进行断言。
parsed_body["foo"].should == "bar"
答案 2 :(得分:45)
response.header['Content-Type'].should include 'application/json'
答案 3 :(得分:34)
还有 json_spec 宝石,值得一看
答案 4 :(得分:11)
简单易行的方法。
# set some variable on success like :success => true in your controller
controller.rb
render :json => {:success => true, :data => data} # on success
spec_controller.rb
parse_json = JSON(response.body)
parse_json["success"].should == true
答案 5 :(得分:7)
你可以查看'Content-Type'
标题,看它是否正确?
response.header['Content-Type'].should include 'text/javascript'
答案 6 :(得分:7)
另一种测试JSON响应的方法(不是内容中的内容包含预期值)是使用ActiveSupport解析响应:
ActiveSupport::JSON.decode(response.body).should_not be_nil
如果响应不可解析JSON,则抛出异常并且测试将失败。
答案 7 :(得分:7)
您还可以在auxData = pd.DataFrame({'invited':[np.nan, '2017-01-01','2017-03-03']})
auxData.invited = pd.to_datetime(auxData.invited, dayfirst=True)
print (auxData)
invited
0 NaT
1 2017-01-01
2 2017-03-03
print (auxData['invited'].notnull())
0 False
1 True
2 True
Name: invited, dtype: bool
auxData['flagInvited'] = auxData['invited'].notnull().astype(int)
print (auxData)
invited flagInvited
0 NaT 0
1 2017-01-01 1
2 2017-03-03 1
spec/support/
并在需要访问JSON响应时使用module ApiHelpers
def json_body
JSON.parse(response.body)
end
end
RSpec.configure do |config|
config.include ApiHelpers, type: :request
end
。
例如,在您的请求规范中,您可以直接使用它
json_body
答案 8 :(得分:4)
当使用Rails 5(目前仍处于测试版)时,测试响应上有一个新方法parsed_body
,它将返回解析为最后一个请求编码的响应。
答案 9 :(得分:1)
如果要利用Rspec提供的哈希值差异,最好解析正文并将其与哈希值进行比较。我找到的最简单的方法:
it 'asserts json body' do
expected_body = {
my: 'json',
hash: 'ok'
}.stringify_keys
expect(JSON.parse(response.body)).to eql(expected_body)
end
答案 10 :(得分:0)
我在这里找到了一个客户匹配器:https://raw.github.com/gist/917903/92d7101f643e07896659f84609c117c4c279dfad/have_content_type.rb
将它放在spec / support / matchers / have_content_type.rb中,并确保在spec / spec_helper.rb
中加载来自支持的东西。Dir[Rails.root.join('spec/support/**/*.rb')].each {|f| require f}
以下是代码本身,以防它从给定链接中消失。
RSpec::Matchers.define :have_content_type do |content_type|
CONTENT_HEADER_MATCHER = /^(.*?)(?:; charset=(.*))?$/
chain :with_charset do |charset|
@charset = charset
end
match do |response|
_, content, charset = *content_type_header.match(CONTENT_HEADER_MATCHER).to_a
if @charset
@charset == charset && content == content_type
else
content == content_type
end
end
failure_message_for_should do |response|
if @charset
"Content type #{content_type_header.inspect} should match #{content_type.inspect} with charset #{@charset}"
else
"Content type #{content_type_header.inspect} should match #{content_type.inspect}"
end
end
failure_message_for_should_not do |model|
if @charset
"Content type #{content_type_header.inspect} should not match #{content_type.inspect} with charset #{@charset}"
else
"Content type #{content_type_header.inspect} should not match #{content_type.inspect}"
end
end
def content_type_header
response.headers['Content-Type']
end
end
答案 11 :(得分:0)
上面的许多答案都有些过时,因此这是RSpec(3.8+)的较新版本的快速摘要。此解决方案未从rubocop-rspec发出警告,并且与rspec best practices内联:
成功的JSON响应由两件事标识:
application/json
假定响应对象是测试的匿名主题,则可以使用Rspec的内置匹配器来验证上述两个条件:
context 'when response is received' do
subject { response }
# check for a successful JSON response
it { is_expected.to have_attributes(content_type: include('application/json')) }
it { is_expected.to have_attributes(body: satisfy { |v| JSON.parse(v) }) }
# validates OP's condition
it { is_expected.to satisfy { |v| JSON.parse(v.body).key?('success') }
it { is_expected.to satisfy { |v| JSON.parse(v.body)['success'] == true }
end
如果您准备命名主题,则可以进一步简化上述测试:
context 'when response is received' do
subject(:response) { response }
it 'responds with a valid content type' do
expect(response.content_type).to include('application/json')
end
it 'responds with a valid json object' do
expect { JSON.parse(response.body) }.not_to raise_error
end
it 'validates OPs condition' do
expect(JSON.parse(response.body, symoblize_names: true))
.to include(success: true)
end
end
答案 12 :(得分:0)
产生一个干净但可能很大的差异:
actual = JSON.parse(response.body, symbolize_names: true)
expected = { foo: "bar" }
expect(actual).to eq expected
真实数据的控制台输出示例:
expected: {:story=>{:id=>1, :name=>"The Shire"}}
got: {:story=>{:id=>1, :name=>"The Shire", :description=>nil, :body=>nil, :number=>1}}
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-:story => {:id=>1, :name=>"The Shire"},
+:story => {:id=>1, :name=>"The Shire", :description=>nil, ...}
(感谢@floatingrock发表评论)
如果您想要一个固定的解决方案,则应避免使用可能引入错误肯定等式的解析器;将响应主体与字符串进行比较。例如:
actual = response.body
expected = ({ foo: "bar" }).to_json
expect(actual).to eq expected
但是第二种解决方案在视觉上不太友好,因为它使用了序列化的JSON,其中包含许多转义的引号。
我倾向于为自己编写一个自定义匹配器,它可以更好地精确定位JSON路径在哪个递归槽上不同。将以下内容添加到您的rspec宏中:
def expect_response(actual, expected_status, expected_body = nil)
expect(response).to have_http_status(expected_status)
if expected_body
body = JSON.parse(actual.body, symbolize_names: true)
expect_json_eq(body, expected_body)
end
end
def expect_json_eq(actual, expected, path = "")
expect(actual.class).to eq(expected.class), "Type mismatch at path: #{path}"
if expected.class == Hash
expect(actual.keys).to match_array(expected.keys), "Keys mismatch at path: #{path}"
expected.keys.each do |key|
expect_json_eq(actual[key], expected[key], "#{path}/:#{key}")
end
elsif expected.class == Array
expected.each_with_index do |e, index|
expect_json_eq(actual[index], expected[index], "#{path}[#{index}]")
end
else
expect(actual).to eq(expected), "Type #{expected.class} expected #{expected.inspect} but got #{actual.inspect} at path: #{path}"
end
end
用法示例1:
expect_response(response, :no_content)
用法示例2:
expect_response(response, :ok, {
story: {
id: 1,
name: "Shire Burning",
revisions: [ ... ],
}
})
示例输出:
Type String expected "Shire Burning" but got "Shire Burnin" at path: /:story/:name
另一个示例输出显示了嵌套数组深处的不匹配:
Type Integer expected 2 but got 1 at path: /:story/:revisions[0]/:version
如您所见,输出将告诉您确切的位置来修复期望的JSON。
答案 13 :(得分:0)
对于您的JSON响应,您应该解析该响应以获得预期结果
例如:parsed_response = JSON.parse(response.body)
您可以检查响应中包含的其他变量,例如
expect(parsed_response["success"]).to eq(true)
expect(parsed_response["flashcard"]).to eq("flashcard expected value")
expect(parsed_response["lesson"]).to eq("lesson expected value")
expect(subject["status_code"]).to eq(201)
我也希望检查JSON响应的键,例如:
expect(body_as_json.keys).to match_array(["success", "lesson","status_code", "flashcard"])
在这里,我们可以使用应匹配器以获得Rspec中的预期结果