└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # The RSpec Style Guide 2 | 3 | This RSpec style guide recommends best practices so that real-world Ruby 4 | programmers can write code that can be maintained by other real-world Ruby 5 | programmers. A style guide that reflects real-world usage gets used, and a 6 | style guide that holds to an ideal that has been rejected by the people it is 7 | supposed to help risks not getting used at all – no matter how good it is. 8 | 9 | The guide is separated into several sections of related rules. I've 10 | tried to add the rationale behind the rules (if it's omitted I've 11 | assumed that is pretty obvious). 12 | 13 | The guide is still a work in progress - some rules are lacking 14 | examples, some rules don't have examples that illustrate them clearly 15 | enough. In due time these issues will be addressed - just keep them in 16 | mind for now. 17 | 18 | You can generate a PDF or an HTML copy of this guide using 19 | [Transmuter](https://github.com/TechnoGate/transmuter). 20 | 21 | ## Table of Contents 22 | 23 | * [RSpec](#rspec) 24 | * [Views](#views) 25 | * [Controllers](#controllers) 26 | * [Models](#models) 27 | * [Mailers](#mailers) 28 | * [Rake Tasks](#rake-tasks) 29 | * [Miscellaneous](#miscellaneous) 30 | * [Contributing](#contributing) 31 | * [Spread the Word](#spread-the-word) 32 | 33 | ## RSpec 34 | 35 | * Try to use just one expectation per example, within reason. 36 | 37 | ```Ruby 38 | # bad 39 | describe ArticlesController do 40 | #... 41 | 42 | describe 'GET new' do 43 | it 'assigns new article and renders the new article template' do 44 | get :new 45 | assigns[:article].should be_a_new Article 46 | response.should render_template :new 47 | end 48 | end 49 | 50 | # ... 51 | end 52 | 53 | # good 54 | describe ArticlesController do 55 | #... 56 | 57 | describe 'GET new' do 58 | it 'assigns a new article' do 59 | get :new 60 | assigns[:article].should be_a_new Article 61 | end 62 | 63 | it 'renders the new article template' do 64 | get :new 65 | response.should render_template :new 66 | end 67 | end 68 | 69 | end 70 | ``` 71 | 72 | * Keep the full spec name (concatentation of the nested descriptions) 73 | grammatically correct. 74 | * Top level: use `describe` with a constant name: `describe User ...` 75 | * 2nd level: use `describe` with a method name: `describe "#awesome?"` 76 | * Inner blocks: use a `context` that starts with `when`: `context "when user is unsubscribed"` 77 | * Example describes the expectation: `it "is false"`. 78 | * Full spec name: "User#awesome? when user is unsubscribed is false" 79 | 80 | * Do not use "should" in our example names. 81 | 82 | ```Ruby 83 | # good 84 | it "returns true" 85 | 86 | # bad 87 | it "should return true" 88 | 89 | * Write expectations at a high level, removed from logic and implementation details. 90 | 91 | ```Ruby 92 | # bad 93 | it "calls more_results if i=0" do 94 | # ... 95 | end 96 | 97 | # good 98 | context "no results are returned by the initial search" do 99 | it "attempts to find more results" do 100 | # ... 101 | end 102 | end 103 | ``` 104 | 105 | * Use `describe` for concepts which don't in themselves vary (e.g. "callbacks, validations, "). Typically these are nouns. 106 | 107 | * Name `describe` blocks as follows: 108 | * use "description" for non-methods 109 | * use pound "#method" for instance methods 110 | * use dot ".method" for class methods 111 | 112 | ```Ruby 113 | class Article 114 | def summary 115 | #... 116 | end 117 | 118 | def self.latest 119 | #... 120 | end 121 | end 122 | 123 | # the spec... 124 | describe Article 125 | describe '#summary' 126 | #... 127 | end 128 | 129 | describe '.latest' 130 | #... 131 | end 132 | end 133 | ``` 134 | 135 | * Do not write iterators to generate tests. 136 | 137 | ```Ruby 138 | # bad 139 | [:new, :show, :index].each do |action| 140 | "it returns 200" do 141 | get action 142 | response.should be_ok 143 | end 144 | end 145 | ``` 146 | 147 | * Use Factory Girl to create test objects. 148 | 149 | * Try to avoid mocking and stubbing, favoring test parameters or 150 | attributes instead. When resorting to mocking and stubbing, only 151 | mock against a small, stable, obvious (or documented) API, so stubs 152 | are likely to represent reality after future refactoring. 153 | 154 | * Valid reasons to use stubs/mocks: 155 | * Performance: To prevent running a slow, unrelated task. 156 | * Determinism: To ensure the test gives the same result each 157 | time. e.g. Time.now, Kernel#rand, external web services. 158 | * Vendoring: When relying on 3rd party code used as a "black box", 159 | which wasn't written with testability in mind. 160 | * Legacy: Stubbing old code that requires complex setup. (New code 161 | should not require complex setup!) 162 | * BDD: To remove the dependence on code that does not yet exist. 163 | * Controller / Functional tests: 164 | 165 | > In a controller spec, we don't care about how our data objects are created or what data they contain; we are writing expectations for the functional behavior of that controller, and that controller only. Mocks and stubs are used to decouple from the model layer and stay focused on the task of specing the controller. 166 | > 167 | > joahking, RailsForum: http://railsforum.com/viewtopic.php?pid=68311#p68311 168 | 169 | * Use `let` blocks instead of `before(:each)` blocks to create data for 170 | the spec examples. `let` blocks get lazily evaluated. 171 | 172 | ```Ruby 173 | # use this: 174 | let(:article) { FactoryGirl.create(:article) } 175 | 176 | # ... instead of this: 177 | before(:each) { @article = FactoryGirl.create(:article) } 178 | ``` 179 | 180 | ### Views 181 | 182 | * The directory structure of the view specs `spec/views` matches the 183 | one in `app/views`. For example the specs for the views in 184 | `app/views/users` are placed in `spec/views/users`. 185 | * The naming convention for the view specs is adding `_spec.rb` to the 186 | view name, for example the view `_form.html.haml` has a 187 | corresponding spec `_form.html.haml_spec.rb`. 188 | * `spec_helper.rb` need to be required in each view spec file. 189 | * The outer `describe` block uses the path to the view without the 190 | `app/views` part. This is used by the `render` method when it is 191 | called without arguments. 192 | 193 | ```Ruby 194 | # spec/views/articles/new.html.haml_spec.rb 195 | require 'spec_helper' 196 | 197 | describe 'articles/new.html.haml' do 198 | # ... 199 | end 200 | ``` 201 | 202 | * The method `assign` supplies the instance variables which the view 203 | uses and are supplied by the controller. 204 | 205 | ```Ruby 206 | # spec/views/articles/edit.html.haml_spec.rb 207 | describe 'articles/edit.html.haml' do 208 | it 'renders the form for a new article creation' do 209 | assign( 210 | :article, 211 | mock_model(Article).as_new_record.as_null_object 212 | ) 213 | render 214 | rendered.should have_selector('form', 215 | method: 'post', 216 | action: articles_path 217 | ) do |form| 218 | form.should have_selector('input', type: 'submit') 219 | end 220 | end 221 | ``` 222 | 223 | * The helpers specs are separated from the view specs in the `spec/helpers` directory. 224 | 225 | ### Controllers 226 | 227 | * Mock the models and stub their methods. Testing the controller should not depend on the model creation. 228 | * Test only the behaviour the controller should be responsible about: 229 | * Execution of particular methods 230 | * Data returned from the action - assigns, etc. 231 | * Result from the action - template render, redirect, etc. 232 | 233 | ```Ruby 234 | # Example of a commonly used controller spec 235 | # spec/controllers/articles_controller_spec.rb 236 | # We are interested only in the actions the controller should perform 237 | # So we are mocking the model creation and stubbing its methods 238 | # And we concentrate only on the things the controller should do 239 | 240 | describe ArticlesController do 241 | # The model will be used in the specs for all methods of the controller 242 | let(:article) { mock_model(Article) } 243 | 244 | describe 'POST create' do 245 | before { Article.stub(:new).and_return(article) } 246 | 247 | it 'creates a new article with the given attributes' do 248 | Article.should_receive(:new).with(title: 'The New Article Title').and_return(article) 249 | post :create, message: { title: 'The New Article Title' } 250 | end 251 | 252 | it 'saves the article' do 253 | article.should_receive(:save) 254 | post :create 255 | end 256 | 257 | it 'redirects to the Articles index' do 258 | article.stub(:save) 259 | post :create 260 | response.should redirect_to(action: 'index') 261 | end 262 | end 263 | end 264 | ``` 265 | 266 | * Use context when the controller action has different behaviour depending on the received params. 267 | 268 | ```Ruby 269 | # A classic example for use of contexts in a controller spec is creation or update when the object saves successfully or not. 270 | 271 | describe ArticlesController do 272 | let(:article) { mock_model(Article) } 273 | 274 | describe 'POST create' do 275 | before { Article.stub(:new).and_return(article) } 276 | 277 | it 'creates a new article with the given attributes' do 278 | Article.should_receive(:new).with(title: 'The New Article Title').and_return(article) 279 | post :create, article: { title: 'The New Article Title' } 280 | end 281 | 282 | it 'saves the article' do 283 | article.should_receive(:save) 284 | post :create 285 | end 286 | 287 | context 'when the article saves successfully' do 288 | before { article.stub(:save).and_return(true) } 289 | 290 | it 'sets a flash[:notice] message' do 291 | post :create 292 | flash[:notice].should eq('The article was saved successfully.') 293 | end 294 | 295 | it 'redirects to the Articles index' do 296 | post :create 297 | response.should redirect_to(action: 'index') 298 | end 299 | end 300 | 301 | context 'when the article fails to save' do 302 | before { article.stub(:save).and_return(false) } 303 | 304 | it 'assigns @article' do 305 | post :create 306 | assigns[:article].should be_eql(article) 307 | end 308 | 309 | it 're-renders the "new" template' do 310 | post :create 311 | response.should render_template('new') 312 | end 313 | end 314 | end 315 | end 316 | ``` 317 | 318 | ### Models 319 | 320 | * Do not mock the models in their own specs. 321 | * Use Factory Girl or Mechanize to make real objects. 322 | * It is acceptable to mock other models or child objects. 323 | * Create the model for all examples in the spec to avoid duplication. 324 | * Avoid unnecessary database calls. 325 | 326 | ```Ruby 327 | describe Article 328 | let(:article) { FactoryGirl.build(:article) } 329 | end 330 | ``` 331 | 332 | * Add an example ensuring that the fabricated model is valid. 333 | 334 | ```Ruby 335 | describe Article 336 | it 'is valid with valid attributes' do 337 | article.should be_valid 338 | end 339 | end 340 | ``` 341 | 342 | * When testing validations, use `have(x).errors_on` to specify the attibute 343 | which should be validated. Using `be_valid` does not guarantee that the problem 344 | is in the intended attribute. 345 | 346 | ```Ruby 347 | # bad 348 | describe '#title' 349 | it 'is required' do 350 | article.title = nil 351 | article.should_not be_valid 352 | end 353 | end 354 | 355 | # preferred 356 | describe '#title' 357 | it 'is required' do 358 | article.title = nil 359 | article.should have(1).error_on(:title) 360 | end 361 | end 362 | ``` 363 | 364 | ### Mailers 365 | 366 | * The mailer spec should verify that: 367 | * the subject is correct 368 | * the receiver e-mail is correct 369 | * the e-mail is sent to the right e-mail address 370 | * testing the email body should be done in a view spec 371 | 372 | ```Ruby 373 | describe SubscriberMailer 374 | let(:subscriber) { FactoryGirl.build(:subscription, email: 'johndoe@test.com', name: 'John Doe') } 375 | 376 | describe 'successful registration email' 377 | subject { SubscriptionMailer.successful_registration_email(subscriber) } 378 | 379 | its(:subject) { should == 'Successful Registration!' } 380 | its(:from) { should == ['info@your_site.com'] } 381 | its(:to) { should == [subscriber.email] } 382 | end 383 | end 384 | ``` 385 | 386 | ### Rake Tasks 387 | 388 | * Rake tasks should be dumb (one-liners ideally), and call out to a model or 389 | library class which is tested like any other. 390 | 391 | ### Miscellaneous 392 | 393 | * Avoid incidental state as much as possible. 394 | ```Ruby 395 | describe Article do 396 | let(:article) { FactoryGirl.create(:article) } 397 | let(:another_article) { FactoryGirl.create(:article) } 398 | 399 | describe "#publish" do 400 | # bad 401 | it "publishes the article" do 402 | article.publish 403 | 404 | # Creating another shared Article test object above would cause this 405 | # test to break 406 | Article.count.should == 2 407 | end 408 | 409 | # good 410 | it "publishes the article" do 411 | -> { article.publish }.should change(Article, :count).by(1) 412 | end 413 | end 414 | end 415 | ``` 416 | * Shared examples are helpful for tidying up repetitive expectations, 417 | but should be written to be composable if possible to allow for situations where 418 | many but not all of the shared expectations are required. 419 | 420 | * Be careful not to focus on being "DRY" by moving repeated expectations 421 | into a shared environment too early, as this can lead to brittle tests 422 | that rely too much on one other. 423 | 424 | # Contributing 425 | 426 | Feel free to open tickets or send pull requests with improvements. Thanks in 427 | advance for your help! 428 | 429 | # Spread the Word 430 | 431 | A community-driven style guide is of little use to a community that 432 | doesn't know about its existence. Tweet about the guide, share it with 433 | your friends and colleagues. Every comment, suggestion or opinion we 434 | get makes the guide just a little bit better. And we want to have the 435 | best possible guide, don't we? 436 | --------------------------------------------------------------------------------