Ruby on rails rspec重构?
我有下面的测试,有两个几乎相同的块。现在我正在寻找方法来干净地重构它 测试:Ruby on rails rspec重构?,ruby-on-rails,rspec,Ruby On Rails,Rspec,我有下面的测试,有两个几乎相同的块。现在我正在寻找方法来干净地重构它 测试: context "with the d1 configuration" do before (:each) do # send a message @envelope = Factory(:envelope, :destination => '32495xxxxxx', :message => 'Message sent by d1') @distributor = Distr
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(在我们的项目中,它们的名称更有意义:)——现在我需要添加第三个分发服务器。我该怎么办
我可以循环一个包含变化部分的数组(在本例中为:分发服务器名称和消息内容)。但是我也可以更改测试名称吗
这里最好的方法是什么?是否可以制作某种测试模板,在其中可以填写某些值并执行这些值 是的,您可以循环遍历一个充满示例的数组/散列,是的,您可以基于此重命名上下文,但您必须注意范围问题-例如,上下文是类级范围,而测试是实例。 因此,您必须在上下文的“设置”部分的实例变量中设置这些内容。 我主要是用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
我和一位更有经验的同事进行了结对编程,我们一起想出了以下解决方案 我们首先定义了一些共享行为:
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
现在,我仍然可以应用塔林提出的建议答案,但我不完全确定是否真的需要它 是的,这一个更好,更像rspec:)更好地定义一组事情应该遵循的常见行为!