Ruby on rails Rspec如何创建一个方法来;“干的”;只是请求的一些参数?

Ruby on rails Rspec如何创建一个方法来;“干的”;只是请求的一些参数?,ruby-on-rails,ruby,testing,rspec,rspec-rails,Ruby On Rails,Ruby,Testing,Rspec,Rspec Rails,我想测试我的项目的create方法,但是这个create方法在我的表单中有3个步骤,我想测试所有步骤。要测试每个步骤,我需要发送一个create请求,其中包含该步骤各自的参数 问题是:我在每个步骤中重复了许多参数,我想知道如何将公共参数放入一个方法中,然后调用它 这是我的rspec文件 require 'rails_helper' RSpec.describe Api::MenteeApplicationsController, type: :controller do describ

我想测试我的项目的create方法,但是这个create方法在我的表单中有3个步骤,我想测试所有步骤。要测试每个步骤,我需要发送一个create请求,其中包含该步骤各自的参数

问题是:我在每个步骤中重复了许多参数,我想知道如何将公共参数放入一个方法中,然后调用它

这是我的rspec文件

require 'rails_helper'

RSpec.describe Api::MenteeApplicationsController, type: :controller do
    describe "Api Mentee Application controller tests" do
        let(:edition) { create(:edition) }

        it 'should start create a Mentee Application, step 1' do
            edition
            post :create, application: {
                first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
                gender: "female", country: "IN", program_country: "IN",
                time_zone: "5 - Mumbai", communicating_in_english: "true",
                send_to_mentor_confirmed: "true",
                time_availability: 3,
                previous_programming_experience: "false" },
                step: "1", steps: "3"

            expect(response).to have_http_status(200)
        end

        it 'should continue to create a Mentee Application, step 2' do
            post :create, application: {
                first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
                gender: "female", country: "IN", program_country: "IN",
                time_zone: "5 - Mumbai", communicating_in_english: "true",
                send_to_mentor_confirmed: "true",
                time_availability: 3,
                motivation: "Motivation",
                background: "Background",
                team_work_experience: "Team Work Experience",
                previous_programming_experience: "false" },
                step: "2", steps: "3"

            expect(response).to have_http_status(200)
        end

        it 'should not create a Mentee Application in api format' do
            applications = MenteeApplication.count
            post :create, application: {
                first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
                gender: "female", country: "IN", program_country: "IN",
                time_zone: "5 - Mumbai", communicating_in_english: "true",
                send_to_mentor_confirmed: "true",
                motivation: "Motivation",
                background: "Background",
                team_work_experience: "Team Work Experience",
                previous_programming_experience: "false", experience: "",
                operating_system: "mac_os",
                project_proposal: "Project Proposal",
                roadmap: "Roadmap",
                time_availability: 3,
                engagements: ["master_student", "part_time", "volunteer", "one_project"] },
            step: "3", steps: "3"

            expect(response).to have_http_status(:unprocessable_entity)
            expect(MenteeApplication.count).to be(0)
        end

        it 'should create a Mentee Application in api format (step 3)' do
            applications = MenteeApplication.count
            post :create, application: {
                first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
                gender: "female", country: "IN", program_country: "IN",
                time_zone: "5 - Mumbai", communicating_in_english: "true",
                send_to_mentor_confirmed: "true",
                motivation: "Motivation",
                background: "Background",
                programming_language: "ruby",
                team_work_experience: "Team Work Experience",
                previous_programming_experience: "false", experience: "",
                operating_system: "mac_os",
                project_proposal: "Project Proposal",
                roadmap: "Roadmap",
                time_availability: 3,
                engagements: ["master_student", "part_time", "volunteer", "one_project"] },
            step: "3", steps: "3"

            expect(response).to have_http_status(200)
            expect(MenteeApplication.count).to be(applications+1)
            expect(flash[:notice]).to eq("Thank you for your application!")
        end

    end
end
如您所见,步骤1中的参数用于步骤2和步骤3,因此我的想法如下:

def some_params
    params.require(:application).permit(first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
            gender: "female", country: "IN", program_country: "IN",
            time_zone: "5 - Mumbai", communicating_in_english: "true",
            send_to_mentor_confirmed: "true",
            time_availability: 3,
            previous_programming_experience: "false")
end
require 'rails_helper'

RSpec.describe Api::MenteeApplicationsController, type: :controller do
    describe "Api Mentee Application controller tests" do
        let(:edition) { create(:edition) }
        let(:first_step_params) do
          {
            first_name: 'Mentee',
            last_name: 'Rspec',
            #...
            previous_programming_experience: false,
          }
        end
        let(:second_step_params) do
          {
            motivation: "Motivation",
            background: "Background",
            team_work_experience: "Team Work Experience",
          }.merge(first_step_params)
        end
        let(:third_step_params) do
          {
            operating_system: "mac_os",
            project_proposal: "Project Proposal",
            roadmap: "Roadmap",
            time_availability: 3,
            engagements: ["master_student", "part_time", "volunteer", "one_project"],
          }.merge(third_step_params)
        end

        it 'should start create a Mentee Application, step 1' do
            edition                                                          

            post :create, application: first_step_params, step: "1", steps: "3"

            expect(response).to have_http_status(200)                        
        end                                                                  

        it 'should continue to create a Mentee Application, step 2' do       
            post :create, application: second_step_params, step: "2", steps: "3"

            expect(response).to have_http_status(200)                        
        end

        it 'should not create a Mentee Application in api format' do
            applications = MenteeApplication.count

            post :create, application: third_step_params, step: "3", steps: "3"

            expect(response).to have_http_status(:unprocessable_entity)
            expect(MenteeApplication.count).to be(0)
        end
    end
end

但是不起作用,我该怎么做呢?

块允许您定义在测试用例中使用的变量(
it
s)。需要注意的一些关键点:

  • 它们是惰性计算的:在调用变量之前,块中的代码不会运行(除非使用bang--
    let!
    --强制计算)
  • 它们可能在内部
    上下文中被重写
前往了解更多关于他们的信息


您提供的代码可以像这样使用
let
s:

def some_params
    params.require(:application).permit(first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
            gender: "female", country: "IN", program_country: "IN",
            time_zone: "5 - Mumbai", communicating_in_english: "true",
            send_to_mentor_confirmed: "true",
            time_availability: 3,
            previous_programming_experience: "false")
end
require 'rails_helper'

RSpec.describe Api::MenteeApplicationsController, type: :controller do
    describe "Api Mentee Application controller tests" do
        let(:edition) { create(:edition) }
        let(:first_step_params) do
          {
            first_name: 'Mentee',
            last_name: 'Rspec',
            #...
            previous_programming_experience: false,
          }
        end
        let(:second_step_params) do
          {
            motivation: "Motivation",
            background: "Background",
            team_work_experience: "Team Work Experience",
          }.merge(first_step_params)
        end
        let(:third_step_params) do
          {
            operating_system: "mac_os",
            project_proposal: "Project Proposal",
            roadmap: "Roadmap",
            time_availability: 3,
            engagements: ["master_student", "part_time", "volunteer", "one_project"],
          }.merge(third_step_params)
        end

        it 'should start create a Mentee Application, step 1' do
            edition                                                          

            post :create, application: first_step_params, step: "1", steps: "3"

            expect(response).to have_http_status(200)                        
        end                                                                  

        it 'should continue to create a Mentee Application, step 2' do       
            post :create, application: second_step_params, step: "2", steps: "3"

            expect(response).to have_http_status(200)                        
        end

        it 'should not create a Mentee Application in api format' do
            applications = MenteeApplication.count

            post :create, application: third_step_params, step: "3", steps: "3"

            expect(response).to have_http_status(:unprocessable_entity)
            expect(MenteeApplication.count).to be(0)
        end
    end
end
补充建议 1.不执行控制器规格 控制器是用户界面和后台服务之间的一个薄软件层。他们的测试很难被认为是集成(端到端)或单元测试

我建议您改为实施功能规格。(非常适合使用RSpec进行Rails测试)

这可能会提供更多关于这方面的见解

2.不要在测试用例描述中使用should 看

3.注意后面的最后一个逗号 它可以防止

4.使用.rspec文件 内容包括

--require rails_helper
因此,您不需要在每个规范文件的顶部添加“rails\u helper”

5.使用
context
s 这也是来自betterspecs.org的指导。你可以这样做

RSpec.describe Api::MenteeApplicationsController, type: :controller do
    describe "Api Mentee Application controller tests" do
        let(:edition) { create(:edition) }
        let(:application_params) do
          {
            #...
          }
        end
        let(:step) { 1 }

        it 'should start create a Mentee Application' do
            edition

            post :create, application: application_params, step: step, steps: "3"

            expect(response).to have_http_status(200)
        end

        context 'in second step' do
          let(:step) { 2 }

          it 'should continue to create a Mentee Application' do
              post :create, application: application_params, step: step, steps: "3"

              expect(response).to have_http_status(200)
          end
        end
    end
end
context
s还可以方便地处理其他参数:

RSpec.describe Api::MenteeApplicationsController, type: :controller do
  describe "Api Mentee Application controller tests" do
    let(:edition) { create(:edition) }
    let(:application_params) do
      common_params.merge(additional_params)
    end
    let(:commom_params) do
      {
        #...
      }
    end
    let(:additional_params) { {} }

    it 'creates an application' do
      post :create, application: application_params
    end

    context 'with API params' do
      let(:additional_params) do
        {
          #...
        }
      end

      it 'creates an application' do
        post :create, application: application_params
      end
    end
  end
end

请注意,
post
方法调用在两种上下文中变得完全相同。这将允许重用它(在
之前的
块中,或者甚至在另一个
块中)。

我想我会尝试像下面这样做。基本上:

  • 创建一个名为
    @full_application
    的记忆变量,并将其包装到一个方法中(我在测试的底部完成了这项工作)

  • 创建常量,规定每个测试所需的值子集,例如
    步骤一参数
    步骤二参数
    ,等等

  • 在每个
    it
    块中,使用
    .slice
    和上面定义的常量从
    full\u应用程序
    中“获取”要使用的值

  • 大概是这样的:

    require 'rails_helper'
    
    RSpec.describe Api::MenteeApplicationsController, type: :controller do
    
      STEP_ONE_PARAMS = %w(
        first_name
        last_name
        email
        gender
        country
        communicating_in_english
        send_to_mentor_confirmed
        time_availability
        previous_programming_experience
      ).freeze
    
      STEP_TWO_PARAMS = STEP_ONE_PARAMS.dup.concat(%w(
        motivation
        background
        team_work_experience
      )).freeze
    
      STEP_THREE_PARAMS = STEP_TWO_PARAMS.dup.concat(%w(
        operating_system
        project_proposal
        roadmap
        engagements
      )).freeze
    
        describe "Api Mentee Application controller tests" do
            let(:edition) { create(:edition) }
    
            it 'should start create a Mentee Application, step 1' do
                edition
                post :create, application: full_application.slice(*STEP_ONE_PARAMS),
                    step: "1", steps: "3"
    
                expect(response).to have_http_status(200)
            end
    
            it 'should continue to create a Mentee Application, step 2' do
                post :create, application: full_application.slice(*STEP_TWO_PARAMS),
                    step: "2", steps: "3"
    
                expect(response).to have_http_status(200)
            end
    
            it 'should not create a Mentee Application in api format' do
                applications = MenteeApplication.count
                post :create, application: full_application.slice(*STEP_THREE_PARAMS),
                step: "3", steps: "3"
    
                expect(response).to have_http_status(:unprocessable_entity)
                expect(MenteeApplication.count).to be(0)
            end
    
            it 'should create a Mentee Application in api format (step 3)' do
                applications = MenteeApplication.count
                post :create, application: full_application,
                step: "3", steps: "3"
    
                expect(response).to have_http_status(200)
                expect(MenteeApplication.count).to be(applications+1)
                expect(flash[:notice]).to eq("Thank you for your application!")
            end
    
        end
    end
    
    
    def full_application
      @full_application ||= {
        first_name:                       "Mentee", 
        last_name:                        "Rspec", 
        email:                            "mentee@email.com",
        gender:                           "female", 
        country:                          "IN", 
        program_country:                  "IN",
        time_zone:                        "5 - Mumbai", 
        communicating_in_english:         "true",
        send_to_mentor_confirmed:         "true",
        motivation:                       "Motivation",
        background:                       "Background",
        programming_language:             "ruby",
        team_work_experience:             "Team Work Experience",
        previous_programming_experience:  "false", 
        experience:                       "",
        operating_system:                 "mac_os",
        project_proposal:                 "Project Proposal",
        roadmap:                          "Roadmap",
        time_availability:                3,
        engagements: [
          "master_student", 
          "part_time", 
          "volunteer", 
          "one_project"
        ] 
      }
    end
    

    但是,这并没有提供新的应用程序参数。谢谢@JohanWentholt。我已经更新了答案。你觉得怎么样?我不想成为令人讨厌的人,但你添加了步骤3参数,但仍然忽略了步骤2。@MatheusSantana这对步骤1有效,但我无法使其对步骤2和步骤3有效。步骤2的代码:post:create,application:application_params{motivation:“motivation”,background:“background”,team_work_experience:“team work experience”},步骤:“2”,步骤:“3”,它会覆盖您正在创建的哈希。这意味着,如果有两个相互冲突的键,则选择前一个参数的值。如果您不希望出现这种行为,则必须更改顺序并在以前的参数上调用#merge,提供新的添加项。为什么在
    arr1+arr2上使用
    arr1.dup.concat(arr2)
    ?或者你不知道数组上的
    +
    方法吗?哦,哇!数组上有一个
    +
    方法?!?顺便说一句,
    concat
    +
    是不同的。一个关于这个话题的问答,虽然有很多。在这个特殊的例子中,OP可能会走任何一条路而不会产生重大后果。