-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path12.1.txt
150 lines (98 loc) · 6.5 KB
/
12.1.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
12.1 Describe It!
RSpec provides a domain-specific language for specifying the behavior of objects. It embraces the metaphor of describing behavior the way we might express it if we were talking to a customer or another developer. A snippet of such a conversation might look like this:
You: Describe a new account.
Somebody else: It should have a balance of zero.
Here’s that same conversation expressed in RSpec:
describe "A new Account" do
it "should have a balance of 0" do
account = Account.new
account.balance.should == Money.new(0, :USD)
end
end
We use the describe( ) method to define an example group. The string we pass to it represents the facet of the system that we want to describe (a new account). The block holds the code examples that make up that group.
The it( ) method defines a code example. The string passed to it describes the specific behavior we’re interested in specifying about that facet (should have a balance of zero). The block holds the example code that exercises the subject code and sets expectations about its behavior.
Using strings like this instead of legal Ruby class names and method names provides a lot of flexibility. Here’s an example from RSpec’s own code examples:
it "matches when actual < (expected + delta)" do
be_close(5.0, 0.5).matches?(5.49).should be_true
end
This is an example of the behavior of code, so the intended audience is someone who can read code. With Test::Unit, we might name the method test_matches_when_value_is_less_than_target_plus_delta, which is pretty readable, but the ability to use nonalphanumeric characters makes the name of this example more expressive.
To get a better sense of how you can unleash this expressiveness, let’s take a closer look at the describe( ) and it( ) methods.
The describe Method
The describe( ) method takes an arbitrary number of arguments and an optional block and returns a subclass of RSpec::Core::ExampleGroup. We typically use only one or two arguments, which represent the facet of behavior that we want to describe. They might describe an object, perhaps in a predefined state or perhaps a subset of the behavior we can expect from that object. Let’s look at a few examples, with the output they produce so we can get an idea of how the arguments relate to each other:
describe "A User" { ... }
=> A User
describe User { ... }
=> User
describe User, "with no roles assigned" { ... }
=> User with no roles assigned
describe User, "should require password length between 5 and 40" { ... }
=> User should require password length between 5 and 40
The first argument can be either a reference to a class or module or a string. The second argument is optional and should be a string. Using the class/module for the first argument provides an interesting benefit: when we wrap ExampleGroup in a module, we’ll see that module’s name in the output. For example, if User is in the Authentication module, we could do something like this:
module Authentication
describe User, "with no roles assigned" do
The resulting report would look like this:
Authentication::User with no roles assigned
So, by wrapping the ExampleGroup in a module, we see the fully qualified name Authentication::User, followed by the contents of the second argument. Together, they form a descriptive string, and we get the fully qualified name for free. This is a nice way to help RSpec help us understand where things live as we’re looking at the output.
You can also nest example groups, which can be a very nice way of expressing things in both input and output. For example, we can nest the input like this:
describe User do
describe "with no roles assigned" do
it "is not allowed to view protected content" do
This produces output like this:
User
with no roles assigned
is not allowed to view protected content
The context Method
The context( ) method is an alias for describe( ), so they can be used interchangeably. We tend to use describe( ) for things and context( ) for context.
The User example, shown earlier, for example, could be written like this:
describe User do
context "with no roles assigned" do
it "is not allowed to view protected content" do
The output would be the same as when we used describe( ) on the second line, but context( ) can make it easier to scan a spec file and understand what relates to what.
What’s It All About?
Similar to describe( ), the it( ) method takes a single string, an optional hash, and an optional block. The string should be a sentence that, when prefixed with “it,” represents the detail that will be expressed in code within the block. Here’s an example specifying a stack:
describe Stack do
before(:each) do
@stack = Stack.new
@stack.push :item
end
describe "#peek" do
it "should return the top element" do
@stack.peek.should == :item
end
it "should not remove the top element" do
@stack.peek
@stack.size.should == 1
end
end
describe "#pop" do
it "should return the top element" do
@stack.pop.should == :item
end
it "should remove the top element" do
@stack.pop
@stack.size.should == 0
end
end
end
This is also exploiting RSpec’s nested example groups feature to group the examples of pop( ) separately from the examples of peek( ).
When run with the --format documentation command-line option, this would produce the following output:
Stack
#peek
should return the top element
should not remove the top element
#pop
should return the top element
should remove the top element
Finished in 0.00154 seconds
4 examples, 0 failures
Looks a bit like a specification, doesn’t it? In fact, if we reword the example names without the word should in them, we can get output that looks even more like documentation:
Stack
#peek
returns the top element
does not remove the top element
#pop
returns the top element removes the top element
Finished in 0.00157 seconds
4 examples, 0 failures
What? No should? Remember, the goal here is readable sentences. Should was the tool that Dan North used to get people writing sentences but is not itself essential to the goal.
The ability to pass free text to the it( ) method allows us to name and organize examples in meaningful ways. As with describe( ), the string can even include punctuation. This is especially useful when we’re dealing with code-level concepts in which symbols have important meaning that can help us understand the intent of the example.