加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

单元测试 – 用于测试多个数据点的rspec设计模式

发布时间:2020-12-14 05:00:18 所属栏目:百科 来源:网络整理
导读:我经常发现,在编写一个方法的测试时,我想在方法中抛出一堆不同的输入,然后检查输出是否符合我的预期. 作为一个简单的例子,假设我正在测试my_square_function,它正方形数字并智能地处理nil. 以下代码似乎可以完成这项工作,但我想知道我是否应该使用最佳实践(
我经常发现,在编写一个方法的测试时,我想在方法中抛出一堆不同的输入,然后检查输出是否符合我的预期.

作为一个简单的例子,假设我正在测试my_square_function,它正方形数字并智能地处理nil.

以下代码似乎可以完成这项工作,但我想知道我是否应该使用最佳实践(例如使用主题,上下文):

describe "my_square_function" do
  @tests = [{:input => 1,:result => 1},{:input => -1,{:input => 2,:result => 4},{:input => nil,:result => nil}]
  @tests.each do |test|
    it "squares #{test[:input].inspect} and gets #{test[:result].inspect}" do
      my_square_function(test[:input]).should == test[:result]
    end
  end
end

建议?

谢谢!

(相关:rspec refactoring?)

解决方法

很抱歉这么长的答案,但我认为如果我完成所有这些,我的思维过程会更加连贯.

由于这个问题用TDD标记,我假设您正在编写方法TDD样式.如果是这种情况,您可能希望从以下开始:

describe "my_square_function" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end
 end

如果测试失败,您可以按如下方式实现my_square_function:

def my_square_function(number)
   1
 end

现在测试正在通过,你想重构重复.在这种情况下,复制是在代码和测试之间,即文字1.由于参数带有测试的值,我们可以通过使用参数来删除重复.

def my_square_function(number)
   number
 end

现在已经删除了重复并且测试仍然通过,我们可以进入下一个测试:

describe "my_square_function" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end

   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end
 end

运行测试再次遇到失败的测试,所以我们通过:

def my_square_function(number)
   number.abs   # of course I probably wouldn't really do this but
                # hey,it's an example. :-)
 end

现在这个测试通过了,现在是时候进行另一个测试了:

describe "my_square_function" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end

   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end

   it "Squares other positive numbers" do
     my_square_function(2).should == 4
   end
 end

此时,您的最新测试将不再通过,所以现在让它通过:

def my_square_function(number)
   number.abs * number
 end

哎呀.这没有用,它导致我们的负数测试失败.幸运的是,失败使我们回到了无效的确切测试,我们知道由于“负面”测试而失败了.回到代码:

def my_square_function(number)
   number.abs * number.abs
 end

那更好,我们现在所有的测试都通过了.是时候重新进行重构了.在这里,我们在abs的调用中看到了一些其他不必要的代码.我们可以摆脱它们:

def my_square_function(number)
   number * number
 end

测试仍然通过,我们看到更多的重复与那个讨厌的论点.让我们看看我们是否可以摆脱它:

def my_square_function(number)
   number ** 2
 end

测试通过,我们不再有重复.现在我们有一个干净的实现,让我们接下来处理nil案例:

describe "my_square_function" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end

   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end

   it "Squares other positive numbers" do
     my_square_function(2).should == 4
   end

   it "Doesn't try to process 'nil' arguments" do
     my_square_function(nil).should == nil
   end
 end

好的,我们又回到了失败状态,我们可以继续执行零检查:

def my_square_function(number)
   number ** 2 unless number == nil
 end

这个测试通过并且非常干净,所以我们将保持原样.现在我们回到规范,看看我们得到了什么,并验证我们喜欢我们所看到的:

describe "my_square_function" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end

   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end

   it "Squares other positive numbers" do
     my_square_function(2).should == 4
   end

   it "Doesn't try to process 'nil' arguments" do
     my_square_function(nil).should == nil
   end
 end

我的第一个倾向是我们真的在描述“平方”数字的行为,而不是函数本身,所以我们将改变:

describe "How to square a number" do
   it "Squares a positive number" do
     my_square_function(1).should == 1
   end

   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end

   it "Squares other positive numbers" do
     my_square_function(2).should == 4
   end

   it "Doesn't try to process 'nil' arguments" do
     my_square_function(nil).should == nil
   end
 end

现在,这三个示例名称在放入上下文时有点松软.我将从第一个例子开始,它似乎对方形1有点俗气.这是我要做的一个选择,以减少代码中的示例数量.我真的希望这些例子在某种程度上很有趣,或者我不会测试它们.平方1和2之间的差异是无趣的,所以我将删除第一个例子.它起初很有用,但不再适用.这让我们:

describe "How to square a number" do
   it "Squares a negative number" do
     my_square_function(-1).should == 1
   end

   it "Squares other positive numbers" do
     my_square_function(2).should == 4
   end

   it "Doesn't try to process 'nil' arguments" do
     my_square_function(nil).should == nil
   end
 end

接下来我要看的是负面的例子,因为它与describe块中的上下文有关.我将给出它和其他示例的新描述:

describe "How to square a number" do
   it "Squaring a number is simply the number multiplied by itself" do
     my_square_function(2).should == 4
   end

   it "The square of a negative number is positive" do
     my_square_function(-1).should == 1
   end

   it "It is not possible to square a 'nil' value" do
     my_square_function(nil).should == nil
   end
 end

既然我们已经将测试用例的数量限制在最有趣的测试用例中,那么我们真的没有太多可以解决的问题.正如我们在上面看到的那样,很清楚知道故障发生在哪一行,以防它是另一个我们没想到会失败的测试用例.通过构建要运行的方案列表,我们失去了该功能,从而使调试失败变得更加困难.现在,我们可以用另一个解决方案中提到的动态生成的块来替换这些示例,但是我们开始失去我们试图描述的行为.

因此,总而言之,通过将测试的场景限制为仅描述系统有趣特征的场景,您对太多场景的需求将会减少.在一个更复杂的系统上,有许多场景可能会突出显示对象模型可能需要另一种外观.

希望有所帮助!

布兰登

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读