在 Ruby 的内核类中添加一个 asser()方法是 Ruby 的惯用做法吗?

我通过在 Ruby 中编写一个相当于 Kent Beck 的 xUnit 的代码来扩展我对 Ruby 的理解。Python (Kent 在其中写入)在该语言中有一个经常使用的 asser()方法。露比不知道。我认为添加这个应该很容易,但是内核是放置它的正确位置吗?

顺便说一句,我知道 Ruby 中存在各种 Unit 框架-这是一个学习 Ruby 习语的练习,而不是“做某事”。

42322 次浏览

What's your reason for adding the assert method to the Kernel module? Why not just use another module called Assertions or something?

Like this:

module Assertions
def assert(param)
# do something with param
end


# define more assertions here
end

If you really need your assertions to be available everywhere do something like this:

class Object
include Assertions
end

Disclaimer: I didn't test the code but in principle I would do it like this.

My understanding is that you're writing your own testing suite as a way of becoming more familiar with Ruby. So while Test::Unit might be useful as a guide, it's probably not what you're looking for (because it's already done the job).

That said, python's assert is (to me, at least), more analogous to C's assert(3). It's not specifically designed for unit-tests, rather to catch cases where "this should never happen".

How Ruby's built-in unit tests tend to view the problem, then, is that each individual test case class is a subclass of TestCase, and that includes an "assert" statement which checks the validity of what was passed to it and records it for reporting.

No it's not a best practice. The best analogy to assert() in Ruby is just raising

 raise "This is wrong" unless expr

and you can implement your own exceptions if you want to provide for more specific exception handling

It's not especially idiomatic, but I think it's a good idea. Especially if done like this:

def assert(msg=nil)
if DEBUG
raise msg || "Assertion failed!" unless yield
end
end

That way there's no impact if you decide not to run with DEBUG (or some other convenient switch, I've used Kernel.do_assert in the past) set.

I think it is totally valid to use asserts in Ruby. But you are mentioning two different things:

  • xUnit frameworks use assert methods for checking your tests expectations. They are intended to be used in your test code, not in your application code.
  • Some languages like C, Java or Python, include an assert construction intended to be used inside the code of your programs, to check assumptions you make about their integrity. These checks are built inside the code itself. They are not a test-time utility, but a development-time one.

I recently wrote solid_assert: a little Ruby library implementing a Ruby assertion utility and also a post in my blog explaining its motivation. It lets you write expressions in the form:

assert some_string != "some value"
assert clients.empty?, "Isn't the clients list empty?"


invariant "Lists with different sizes?" do
one_variable = calculate_some_value
other_variable = calculate_some_other_value
one_variable > other_variable
end

And they can be deactivated, so assert and invariant get evaluated as empty statements. This let you avoid performance problems in production. But note that The Pragmatic Programmer: from journeyman to master recommends against deactivating them. You should only deactivate them if they really affect the performance.

Regarding the answer saying that the idiomatic Ruby way is using a normal raise statement, I think it lacks expressivity. One of the golden rules of assertive programming is not using assertions for normal exception handling. They are two completely different things. If you use the same syntax for the two of them, I think your code will be more obscure. And of course you lose the capability of deactivating them.

Some widely-regarded books that dedicate whole sections to assertions and recommend their use:

Programming with assertions is an article that illustrates well what assertive programming is about and when to use it (it is based in Java, but the concepts apply to any language).