在测试 RSpec 中接受块的函数时,如何清楚地表明意图?
我刚刚进入 RSpec 并尝试了一些简单的示例,并实现了具有可访问节点的树节点结构。
我用来用 bdd 刷新代码的第一个测试是:
describe "Tree" do
it "is visitable" do
t = Tree.new
visited = nil
t.visit { |n| visited = n }
visited.should == t
end
end
这让我得到了以下实现:
class Tree
def visit(&block)
block.call self
end
end
此时我对 RSpec 代码不太满意,因为它并没有真正显示我正在尝试的意图做得非常清楚,即使它在技术上可行。当我继续实现子项时,它变得更加混乱:
it "has visitable children" do
c1, c2 = Tree.new, Tree.new
t = Tree.new([c1, c2])
visited = Set.new
t.visit { |n| visited.add(n) }
visited.should == Set.new([t, c1, c2])
end
这让我得到了完整的实现:
class Tree
attr_accessor :children
def initialize(children=[])
@children = children
end
def visit(&block)
block.call self
children.each { |c| c.visit &block }
end
end
我对最终的实现感到非常满意(作为一个探索性示例等),但是是否有一个 RSpec 习惯用法可以使规范更加完整有意且易于阅读?
编辑:为了澄清,我想知道是否有好的方法可以使用 RSpec 助手/模拟等来处理这个问题。
I'm just getting into RSpec and playing around a bit with some easy examples and implementing a tree node structure with visitable nodes.
The first test I used to flush out the code with bdd was:
describe "Tree" do
it "is visitable" do
t = Tree.new
visited = nil
t.visit { |n| visited = n }
visited.should == t
end
end
This gets me the following implementation:
class Tree
def visit(&block)
block.call self
end
end
I'm not too happy with the RSpec code at this point, as it doesn't really show the intention of what I'm trying to do very clearly, even though it works technically. When I move on to implement children, it gets even messier:
it "has visitable children" do
c1, c2 = Tree.new, Tree.new
t = Tree.new([c1, c2])
visited = Set.new
t.visit { |n| visited.add(n) }
visited.should == Set.new([t, c1, c2])
end
This gets me the complete implementation:
class Tree
attr_accessor :children
def initialize(children=[])
@children = children
end
def visit(&block)
block.call self
children.each { |c| c.visit &block }
end
end
I'm happy enough with the resulting implementation (being an exploratory example and all), but is there an RSpec idiom that can make the spec more intentional and easily read?
Edit: To clarify, I'm wondering if there are good ways of handling this with RSpec helpers/mocks etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为如果测试看起来也正确那就更好了。真正的问题是为什么要进行 TDD?在编写代码之前定义用例(一般来说)并定义 API 以使其适合您的用例。至少这是我的经验:通过将 TDD 与 RSpec 或其他任何东西一起使用,我最终为我的代码定义了更好的接口。这是因为需要测试尽可能多的功能,需要以允许轻松访问单独部分(并在需要时进行模拟)的方式削减 API。
所以实际上我希望测试用例看起来像生产代码:因为它们包含与生产代码完全相同的 API。如果它最终变得混乱,那么这可能只意味着您还没有准备好使用您的 API?
使用模拟也有帮助,因为它表达了您的期望,就像
回答评论:中一样,这里是模拟测试用例中的块的示例,明确地说明了对该块的期望:
I would argue that it is better if the tests also look right. The real question is why do TDD at all? To define you use cases (generally speaking) before you write the code and to define API so that it fits you use cases. At least this is my experience: by using TDD with RSpec or whatever I end up defining better interfaces to my code. This because the need to test as much of the functionality as possible requires to cut the API in a way that allows to access separate parts easily (and mock if needed).
So actually I would expect test cases to look like production code: because they would contain exact the same API as the production code. If it ends up messy, then it may only mean that you are yet not ready with your API?
Using mocks helps, too, because it expresses your expectations, like in
Answering the comment: here are examples of mocking a block in your test case, explicitely stating expectations against the block:
这看起来或多或少是正确的。测试往往看起来比应用程序代码更混乱,因为它们实际上只是代码应如何工作的可运行的一次性示例。所以我不会过分沉迷于美化你的代码。
也就是说,您可以通过将对象设置放入 before(:each) 块中来稍微清理示例和/或将示例分离到单节点树和带有子节点的树的上下文中。像这样的事情:
That looks more or less correct. Tests tend to look messier than application code, since they're really just runnable one-off examples of how your code should work. So I wouldn't obsess over beautifying your code too much.
That said you could clean up the examples a bit by putting object setup into a before(:each) block and/or separate your examples into contexts for single node trees and trees with children. Something like this: