相当于 Ruby 1.9.2 Test::Unit 中 nUnit 的 TestCase

发布于 2024-10-30 05:04:55 字数 1170 浏览 0 评论 0原文

不久前,当我决定学习 Ruby 时,我还决定开始使用单元测试和 TDD 方法。我开始使用 Test::Unit 并编写了几个非常小的类来了解一般的单元测试,特别是 Ruby 和 Test::Unit。

到目前为止,一切都很简单,但后来我想做一些类似于 nUnit 的 TestCase 的事情,因为我进行了 20 个测试,其中唯一改变的是输入和输出。

代码如下所示:

def test_2_inserted_return_true
    actual = @prime_generator.is_prime?(2)
    assert_equal(true, actual)
  end

  def test_3_inserted_return_true
      actual = @prime_generator.is_prime?(3)
      assert_equal(true, actual)
    end

  def test_5_inserted_return_true
    actual = @prime_generator.is_prime?(5)
    assert_equal(true, actual)
  end

从 DRY 的角度来看,这是相当可怕的。我想要的是类似于 nUnit 的 TestCase 的东西。

像这样的事情:

[TestCase(2.5d, 2d, Result=1.25d)]
[TestCase(-2.5d, 1d, Result = -2.5d)]
public double ValidateDivision(double numerator, double denominator)
{
    var myClass = new MyClass();
    return myClass.Divide(numerator,denominator);
}

我尝试过谷歌搜索,但找不到有关 Test::Unit 的任何信息。我在 RSpec 和 Selenium 上找到了一些,但这并没有真正帮助我。我也尝试在这里搜索,但也找不到任何东西。

我想过让测试方法接受参数,但再次像这样定义方法......对此不满意。此外,如果我没记错的话,这甚至是不可能的(我现在无法测试,或者我会测试)。

所以,我的问题是:是否可以使用 Ruby (1.9.2) 中的 Test::Unit 进行数据驱动测试?如果不能那么什么框架可以做到这一点?

When I decided to learn Ruby a short while ago I also decided that I'd start using unit tests and the TDD methodology. I started out using Test::Unit and wrote a couple of very small classes to get a feel for unit testing in general and Ruby and Test::Unit in particular.

So far everything has been quite easy but then I wanted to do something similar to nUnit's TestCase as I had 20 tests where the only thing that changed was the input and output.

The code looks like this:

def test_2_inserted_return_true
    actual = @prime_generator.is_prime?(2)
    assert_equal(true, actual)
  end

  def test_3_inserted_return_true
      actual = @prime_generator.is_prime?(3)
      assert_equal(true, actual)
    end

  def test_5_inserted_return_true
    actual = @prime_generator.is_prime?(5)
    assert_equal(true, actual)
  end

Which is quite horrible from a DRY-perspective. What I would want is something similar to nUnit's TestCase.

Something like this:

[TestCase(2.5d, 2d, Result=1.25d)]
[TestCase(-2.5d, 1d, Result = -2.5d)]
public double ValidateDivision(double numerator, double denominator)
{
    var myClass = new MyClass();
    return myClass.Divide(numerator,denominator);
}

I've tried googling but could not find anything about Test::Unit. I've found some on RSpec and Selenium but that doesn't really help me. I also tried searching here but couldn't find anything either.

I thought about making the test method take parameters but defining methods again like that... Not happy about it. Besides, if I remember correctly it wasn't even possible (I am not able to test now or I would).

So, my question is: Is it possible to do data driven testing with Test::Unit in Ruby (1.9.2)? If not then what frameworks can do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

魂牵梦绕锁你心扉 2024-11-06 05:04:55

我们不要忘记好的循环:

def test_that_the_first_few_primes_are_detected_as_prime
  [2, 3, 5, 7, 11, 13, 17].each do |p|
    assert @primality_tester.prime?(p)
  end
end

有些人会使用元编程来定义单独的测试方法,但在这种情况下,我认为这是矫枉过正:

[2, 3, 5, 7, 11, 13, 17].each do |p|
  define_method :"test_that_#{p}_is_detected_as_prime" do
    assert @primality_tester.prime?(p)
  end
end

通常,我认为测试不应该是 DRY。它们应该是 DAMP(描述性且有意义的短语)。毕竟,测试是您的规范和文档,并且它们是由可能不熟悉 Ruby 甚至一般编程的人阅读的。所以,我什至不确定你原来的例子是坏的,特别是如果你像上面那样清理它:

def test_that_2_is_detected_as_prime
  assert @primality_tester.prime?(2)
end

def test_that_3_is_detected_as_prime
  assert @primality_tester.prime?(3)
end

def test_that_5_is_detected_as_prime
  assert @primality_tester.prime?(5)
end

这就是我所做的:

  • 重命名测试:现在,测试名称形成一个完整的句子,并且它不不要简单地重述测试的作用。
  • 使用 assert 而不是 assert_equal:检查是否等于 true 几乎从来都不是一个好主意,并且 assert < em>已经检查了结果是否真实,那为什么还要麻烦呢?
  • @prime_generator 重命名为 @primality_tester,因为它不会生成素数,它只检查一个数字是否是素数。
  • is_prime? 重命名为 prime?,因为问号已经暗示了这个问题
  • 最后但同样重要的是:修复格式,因为不仅格式不一致使用标准的 Ruby 编码约定,它甚至在本身中都不一致。

但是,在我看来,最佳解决方案将是这样的:

def test_that_the_first_few_primes_are_detected_as_prime
  assert @primality_tester.prime?(2)
  assert @primality_tester.prime?(3)
  assert @primality_tester.prime?(5)
end

其中存在重复,但正是重复保持测试的可读性和发音是必要的。 DRY 对于生产代码来说是一个很好的原则,它会不断发展和扩展,但对于测试来说,DRY 始终需要与 DAMP 进行平衡。 (也许编写自定义断言是有意义的,但我对此表示怀疑。至少我无法想出一个好的名称,这始终是一个提示。)

一种完全不同的方法将是基于属性的检查,类似于 Haskell 的 QuickCheck 或 .NET 的 Pex。 QuickCheck 有一个 Ruby 端口,名为 RushCheck,但它自 2006 年以来一直无人维护,维护工作只进行了几次几周前,仍然有很多工作要做,以加快最新版本 Ruby 的速度。

Let's not forget about good ol' loops:

def test_that_the_first_few_primes_are_detected_as_prime
  [2, 3, 5, 7, 11, 13, 17].each do |p|
    assert @primality_tester.prime?(p)
  end
end

Some people would use metaprogramming to define separate test methods, but in this case I think it's overkill:

[2, 3, 5, 7, 11, 13, 17].each do |p|
  define_method :"test_that_#{p}_is_detected_as_prime" do
    assert @primality_tester.prime?(p)
  end
end

Generally, I don't think tests should be DRY. They should be DAMP (descriptive and meaningful phrases). After all, tests are your specification and your documentation, and they are read by people who may not be familiar with Ruby or even programming in general. So, I'm not even sure that your original example is bad, especially if you clean it up a bit like I did above:

def test_that_2_is_detected_as_prime
  assert @primality_tester.prime?(2)
end

def test_that_3_is_detected_as_prime
  assert @primality_tester.prime?(3)
end

def test_that_5_is_detected_as_prime
  assert @primality_tester.prime?(5)
end

Here's what I did:

  • Rename the tests: Now, the test name forms a complete sentence, and it doesn't simply restate what the test does.
  • Use assert instead of assert_equal: Checking for equality to true is almost never a good idea anyway, and assert already checks that the result is truthy, so why bother?
  • Rename @prime_generator to @primality_tester, since, well, it doesn't generate primes, it only checks whether a number is prime.
  • Rename is_prime? to just prime?, since the question is already implied by the question mark
  • Last, but certainly not least: Fix the formatting, since not only was the formatting inconsistent with standard Ruby coding conventions, it wasn't even consistent within itself

However, the best solution IMO would be something like this:

def test_that_the_first_few_primes_are_detected_as_prime
  assert @primality_tester.prime?(2)
  assert @primality_tester.prime?(3)
  assert @primality_tester.prime?(5)
end

There's duplication in there, but it is duplication that is necessary to keep the test readable and pronouncable. DRY is a good principle for production code, which evolves and is extended, but for tests, DRY always needs to be balanced against DAMP. (Maybe it would make sense to write a custom assertion, but I doubt it. At least I can't come up with a good name for one, which is always a hint.)

A completely different approach would be property-based checking similar to Haskell's QuickCheck or .NET's Pex. There's a Ruby port of QuickCheck called RushCheck, but it has been unmaintained since 2006 and maintenance has only picked up a couple of weeks ago with still a lot of work left to do to get it up to speed on recent versions of Ruby.

尹雨沫 2024-11-06 05:04:55

您应该检查 rspec 进行单元测试:

require 'prime'

describe "Numbers List" do
  [2, 3, 5, 7, 11, 13, 17].each do |i|
    it "#{i} is prime" do
      i.prime?
    end
  end
end

运行产量:

scratch-1.9.2> rspec -cfn primes.rb 

Numbers List
  2 is prime
  3 is prime
  5 is prime
  7 is prime
  11 is prime
  13 is prime
  17 is prime

Finished in 0.00146 seconds
7 examples, 0 failures

You should check out rspec for unit testing:

require 'prime'

describe "Numbers List" do
  [2, 3, 5, 7, 11, 13, 17].each do |i|
    it "#{i} is prime" do
      i.prime?
    end
  end
end

running yields:

scratch-1.9.2> rspec -cfn primes.rb 

Numbers List
  2 is prime
  3 is prime
  5 is prime
  7 is prime
  11 is prime
  13 is prime
  17 is prime

Finished in 0.00146 seconds
7 examples, 0 failures
断念 2024-11-06 05:04:55

我通过元编程实现了这一点,使用了我在 Jay Fields 博客上找到的一些示例。效果很好,但是对 def_each 的调用必须作为我的测试用例文件的第一行完成。希望这个例子对某人有帮助,很想看到这个答案,但是学习 Define_method 非常酷:)

def self.def_each(method_names, &block)
 method_names.each do |method_name|
  method_name_fixed="test_"+method_name     
  define_method method_name_fixed do
    instance_exec method_name, &block
   end
 end
end

@@servers = ["pixeltmp01.fetchback.com","pixeltmp02.fetchback.com","pixeltmp03.fetchback.com"]

# generate test methods using fixtures and any other testcase-specific data
def_each @@servers do |method_name|
 @fb.withPixelServer(method_name)
 self.LandingPixelM
end

I did this via metaprogramming, using some samples I found on Jay Fields blog. Worked great, but the call to def_each had to be done as the first line of my test case file. Hope this example helps someone, would have loved to have seen this answer but learning about define_method was pretty cool :)

def self.def_each(method_names, &block)
 method_names.each do |method_name|
  method_name_fixed="test_"+method_name     
  define_method method_name_fixed do
    instance_exec method_name, &block
   end
 end
end

@@servers = ["pixeltmp01.fetchback.com","pixeltmp02.fetchback.com","pixeltmp03.fetchback.com"]

# generate test methods using fixtures and any other testcase-specific data
def_each @@servers do |method_name|
 @fb.withPixelServer(method_name)
 self.LandingPixelM
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文