代码之家  ›  专栏  ›  技术社区  ›  nathanvda

RSPEC重构?

  •  3
  • nathanvda  · 技术社区  · 14 年前

    我有下面的测试,有两个几乎相同的模块。现在,我正在寻找方法来彻底地重构它。

    测试:

    context "with the d1 configuration" do
      before (:each) do
        # send a message 
        @envelope = Factory(:envelope, :destination => '32495xxxxxx', :message => 'Message sent by d1')
        @distributor = Distributor.find_by_name(Distributor::D1)
        @result = @envelope.send_to(@distributor)
      end
      it "should created a new sms-message" do
        @envelope.sent_messages.size.should == 1
      end
    
      it "should have created one sms-message linked to the envelope and distributor" do
        sms = @envelope.sent_messages.find_by_distributor_id(@distributor.id)
        sms.should be_instance_of(SentMessage)
        sms.external_message_id.should_not == nil
        sms.sent_message_status_id.should == SentMessageStatus::IN_PROGRESS
      end
    
      it "should add a logline for the creation of the sms-message" do
        @envelope.log_lines.size.should == 2
        @envelope.log_lines.last.message.should =~ /^Sent message/
      end
    end
    
    
    context "with the correct d2 configuration" do
      before (:each) do
        # send a message 
        @envelope    = Factory(:envelope, :destination => '32495xxxxxx', :message => 'Message sent by d2')
        @distributor = Distributor.find_by_name(Distributor::D2)
        @result = @envelope.send_to(@distributor)
      end
      it "should created a new sms-message" do
        @envelope.sent_messages.size.should == 1
      end
    
      it "should have created one sms-message linked to the envelope and distributor" do
        sms = @envelope.sent_messages.find_by_distributor_id(@distributor.id)
        sms.should be_instance_of(SentMessage)
        sms.external_message_id.should_not == nil
        sms.sent_message_status_id.should == SentMessageStatus::IN_PROGRESS
      end
    
      it "should add a logline for the creation of the sms-message" do
        @envelope.log_lines.size.should == 2
        @envelope.log_lines.last.message.should =~ /^Sent message/
      end
    end
    

    如您所知,两个相同的代码块,每个代码块用于不同的分发服务器,d1和d2(在我们的项目中,它们有更多有意义的名称:)),现在我需要添加第三个分发服务器。我该怎么办?

    我可以循环访问包含更改部分的数组(在本例中是:分发服务器名称和消息内容)。但是我也可以更改测试名称吗?

    这里最好的方法是什么?是否可以制作某种测试模板,在那里您可以填写某些值并执行这些值?

    2 回复  |  直到 14 年前
        1
  •  2
  •   Taryn East    14 年前

    是的,您可以通过一个充满示例的数组/散列进行循环,并且可以基于该数组/散列重命名上下文,但是您必须了解范围问题-例如,上下文是一个类级范围,而测试是一个实例。 因此,您必须在上下文的“设置”部分的实例变量中设置这些内容。 我主要是用Unit做这些事情的:test+shoulda(而不是rspec),所以我可能稍微弄乱了范围规则,但它们应该是相似的。

    注意:我还没有测试下面的代码,所以它可能是此类问题的牺牲品…

    # name this better than I have    
    CONFIGS = {'d1' => {:name => Distributor::D1
                        :destination => '32495xxxxxx',
                        :message => 'd1 message'}, 
               'd2' => {:name => Distributor::D2
                        :destination => '98765xxxxxx',
                        :message => 'd2 message'}
               } # etc
    
    CONFIGS.each do |display_name, dist_hash|
      context "with the #{display_name} configuration" do
        before (:each) do
          # scope the value-hash here to make it available to test-cases 
          # (you don't have to if you're just using it in the setup section)
          @dist_hash = dist_hash
          # send a message 
          @envelope = Factory(:envelope, :destination => @dist_hash[:destination], :message => @dist_hash[:message])
          @distributor = Distributor.find_by_name(@dist_hash[:name])
          @result = @envelope.send_to(@distributor)
        end
        it "should created a new sms-message" do
          @envelope.sent_messages.size.should == 1
        end
    
        it "should have created one sms-message linked to the envelope and distributor" do
          sms = @envelope.sent_messages.find_by_distributor_id(@distributor.id)
          sms.should be_instance_of(SentMessage)
          sms.external_message_id.should_not == nil
          sms.sent_message_status_id.should == SentMessageStatus::IN_PROGRESS
        end
    
        it "should add a logline for the creation of the sms-message" do
          @envelope.log_lines.size.should == 2
          @envelope.log_lines.last.message.should =~ /^Sent message/
        end
      end
    end
    
        2
  •  4
  •   nathanvda    14 年前

    我和一个更有经验的学院进行了一次结对编程会议,我们一起提出了以下解决方案。

    我们首先定义了一些共享行为:

    subject {@envelope}
    let(:the_sent_message){ @envelope.sent_messages.find_by_distributor_id(@distributor.id)}
    
    shared_examples_for "a typical sent envelope" do
      it{should have(1).sent_messages }
      it{should have(2).log_lines     }
    end
    
    shared_examples_for "a successful delivery" do
      it("should have 1 IN_PROGRESS sms-message") { the_sent_message.should be_in_progress }
    
      it "should have 1 sms-message with external ref" do
        the_sent_message.external_message_id.should_not == nil
      end
    
      it "should log the delivery success" do
        @envelope.log_lines.last.message.should =~ /^Sent message/
      end
    end
    
    shared_examples_for "a failing delivery" do
      it("should have 1 FAILED sms-message") { the_sent_message.should be_failed }
    
      it "should have 1 sms-message and no external ref" do
        the_sent_message.external_message_id.should == nil
      end
    
      it "should log the delivery failure" do
        @envelope.log_lines.last.message.should =~ /^Failed to send/
      end
    end
    

    然后测试变得更加可读!

    context "delivered by d1" do
      before do
        @distributor = Distributor.find_by_name(Distributor::D1)
    
        send_a_test_envelope_to(@distributor)
      end
    
      it_should_behave_like "a typical sent envelope"
      it_should_behave_like "a successful delivery"
    end
    
    context "delivered by d2" do
      before do
        @distributor = Distributor.find_by_name(Distributor::D2)
    
        send_a_test_envelope_to(@distributor)
      end
    
      it_should_behave_like "a typical sent envelope"
      it_should_behave_like "a successful delivery"
    end
    

    我们还提取了以下方法

    def send_a_test_envelope_to(distributor)
      @envelope = Factory(:envelope, :destination => '32495xxxxxx', :message => "Message sent by #{@distributor.name}")
      @envelope.send_to(distributor)
    end
    

    现在我仍然可以应用建议的答案@taryn proposed,但我不完全确定它是否真的需要。