测试 Grape + RSpec 测试 如何 mock

tsinghan · 2014年11月03日 · 最后由 yfractal 回复于 2014年11月07日 · 4404 次阅读
it "creates a conference" do
     allow(@club).to receive(:is_admin?).and_return(true)
     post "/api/v1/conferences", @conference_params, {"HTTP_API_KEYAPI_KEY" => @member.api_token}
     expect(response.status).to eq(201)
 end
 post  do
        authenticate!
        club = fetch_club 
        if club.is_admin?(current_member)
           xxxxxxxx
        end
end

我想 mock club.is_admin? 这个方法,但是不管用。。。。应该如何 mock?

club = fetch_club 是不是要改成 @club = fetch_club ?

#1 楼 @kayakjiang 不是这个原因,在下面那个方法里面 用不到@实例变量

allow(@club) 那这个 @club 从哪里来的?

#3 楼 @kayakjiang 恩 这个是 在 before(:each) 里面 create 出来的

before(:each) do
     @club   = create(:public_community)
   end

试下这个 allow(instance_double(@club.class)).to receive(:is_admin?).and_return(true) 或者 allow(instance_double(@club)).to receive(:is_admin?).and_return(true)

@TsingHan allow(@club).to receive(:is_admin?).and_return(true) 只是 stub 了一个名叫@club的变量,它跟club = fetch_club 说的不是同一个,故而无用。

假定fetch_club方法是Club.find,即 club 是 Club 的一个 instance, 你可以这样 stub

Club.any_instance.stub(:is_admin?).and_return(true)

Hope these help.

#6 楼 @JIAZHEN 额 , fetch_club 方法 Club.where.first,我在测试中设置了个断点

before(:each) do
     @club   = create(:public_community)
     @member = @club.member
   end

   it "creates a conference" do
     Club.any_instance.stub(:is_admin?).and_return(true)
     binding.pry
     post "/api/v1/conferences", @conference_params, {"HTTP_API_KEYAPI_KEY" => @member.api_token}
     expect(response.status).to eq(201)
   end

在断点处调试 @club.is_admin?(@member.id) => false

这里直接输入 false 了,哪里不对?

#5 楼 @kayakjiang 换了这种也不行,我感觉@JIAZHEN 这种说法 应该是对的,但是实际还是返回 false

感觉 测试 mock 的 club 实例 和 程序中的不是同一个

#6 楼 @JIAZHEN 我把测试改了一下,在测试中分别 mock fetch_club, @club.is_admin? 但是 if @club.is_admin?还是返回 false, controller.stub(:fetch_club).and_return(@club) 这种 mock 对吗?

it "creates a conference" do
     @club.stub(:is_admin?).and_return(true)
     controller.stub(:fetch_club).and_return(@club)
     post "/api/v1/conferences", @conference_params, {"HTTP_API_KEYAPI_KEY" => @member.api_token}
     expect(response.status).to eq(201)
   end

api 代码也同时改了

  post  do
        authenticate!
        @club = fetch_club 
        if @club.is_admin?(current_member.id)
          xxxxxx
        end
end

额 mock 出来了,问题就是在测试中 mock 的 club 和 api 代码中不是同一个实例 fetch_club 方法是定义在 grape helper 里面 所以就 mock fetch_club https://github.com/intridea/grape#stubbing-helpers 这样 就 ok 了,谢谢帮忙回答问题的几位~~~

before do
    Grape::Endpoint.before_each do |endpoint|
      endpoint.stub(:fetch_club).and_return(@club)
    end
  end


  it "creates a conference" do
    @club.stub(:is_admin?).and_return(true)
    post "/api/v1/conferences", @conference_params, {"HTTP_API_KEYAPI_KEY" => @member.api_token}
    expect(response.status).to eq(201)
  end

#7 楼 @TsingHan @user生成于你 stub 之前,所以你此时不成功。

before do
 Club.any_instance.stub(:is_admin?).and_return(true)
 @club   = create(:public_community)
   @member = @club.member
end

it "creates a conference" do
      post "/api/v1/conferences", @conference_params, {"HTTP_API_KEYAPI_KEY" => @member.api_token}
      expect(response.status).to eq(201)
    end

应该就可以了

#12 楼 @JIAZHEN 我按照你上面的写法试了试,我感觉 Club.any_instance.stub(:is_admin?).and_return(true) 这种写法应该可以,但返回的还不是 mock 的 true。

我的程序代码是这么写的

@club = fetch_club 
if @club.is_admin?(current_member.id)
  xxxxx
end

我感觉是因为 fetch_club(写在 grape 的 helper 里) 这块的问题?我上面是用下面这种方式来 mock fetch_club,这样就正常

Grape::Endpoint.before_each do |endpoint|
   endpoint.stub(:fetch_club).and_return(@club)
 end

@TsingHan Sorry for the delay. 今天刚刚看到。

Ahh, 原因是在fetch_club方法。它不是直接查询数据库或者直接使用 Club Object. 而是用 Grape 调用一个 api call。所以你应该 mock 的是Grape::Endpoint. 这里的信息 miss 了。

你用 mock 的时候,应该想清楚到底要 mock 哪个 object. 另外,RSpec 支持 verify_double, 完善了以前double的一个漏洞。FYI - http://relishapp.com/rspec/rspec-mocks/docs/verifying-doubles

为了保持可测还可以重构代码。 拿是否好测作为代码标准,远比所谓的经验要好用。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号