Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting bounds between rspec and cucumber in Rails

I like the way Cucumber connects user specifications to integration testing. The Cucumber part is more or less clear to me.
I struggle when it comes to what should be tested with rspec (non-integration testing) and what shouldn't.
Is it right to unit-test with rspec something that has already been tested with Cucumber (e.g. unit-test will 100% fail if Cucumber test fails, and unit-test will 100% succeed if Cucumber test succeeds)?

To be specific, I have three examples I'd like to resolve.

  1. Here is a case from the RSpec book. They have the following Cucumber scenario:

    Given I am not yet playing
    When I start a new game
    Then I should see "Welcome to Codebreaker!"
    And I should see "Enter guess:"
    

    They build two rspec-tests right after:

    describe "#start" do
         it "sends a welcome message" do
    
         end
         it "prompts for the first guess" do
    
         end
    end
    
  2. Another example is testing routing or action redirect, while there is the following scenario:

    Given I am at the login page
    When I fill in the right username and password
    Then I should be at the index page
    
  3. Sometimes we test helpers that are already tested with Cucumber:

    Given Mike has spent 283 minutes online
    When I go to the Mike's profile page
    Then I should see "4:43" for "Time online:"
    

    I should probably test helper that breaks 283 minutes into "4:43", but it turns out that it is already tested with Cucumber.

It may not be the best examples, but it illustrates what I am talking about.
To me those tests are duplicates.

Could you please comment on the examples above?

Is there any principles or guidelines on what should be tested with rspec, when there are Cucumber tests already?

like image 545
Alex Smolov Avatar asked Mar 30 '13 23:03

Alex Smolov


1 Answers

All this is only my personal opinion on this broad and open topic. Some people might disagree and funny enough they can.

As of general guidelines you should use Cucumber to test entire application stack, something that is called user experience. This application stack might be composed of many smaller independent objects but, same as user using your application wouldn't be interested in those details, your cucumber test shouldn't care for them and focus instead on outer layer of your application.

RSpec( in your setup!) should on the other hand put a main focus on those small objects, building blocks of your application.

There is a big problem with small application examples from the books: They are to small!

Boarder between outer layer of your application and its interiors is to blurred. Entire application is build with two objects! It is hard to distinguished what should test what. As your application grow in size its getting more obvious what is a user experience test(cucumber) and what is object - state test/message expectation test(RSpec).

Using your second example:

With this Cucumber story:

Given I am at the login page
When I fill in the right username and password
Then I should be at the index page

Rspec:

You will probably have some sort of User model:

  • test user name syntax(must start with capital for example)
  • test password must be 7 characters have numbers etc...

You can have some sort of authentication object:

  • test for valid and invalid logins etc
  • test for exceptions being thrown ....

What if your Authentication database is on different server?

  • mock connection and authentication database and test if database receive an request from authentication object.

And yada yada yada... Forever your cucumber test will guard general purpose of your application user login. Even when you add or change behaviour, under the hood, as long as this test pass you can be confident that user can login.

  • you should test things once(in one place)
  • object its responsible for testing its incoming massage(object public interface) for state(return value)
  • when your object depends on other object(sends its a message) don't test it for state(return value) that object is responsible for it.
  • when your object depends on other object(sends its a message) mock that second object and test if it receive your message.

Generally your question is too broad and you wont find a single answer, different people will have different ideas. What you should focus on is to test, as good as you can and with time you will definitely find right way for you. Not a great test suit is much better then none.

Because good design helps to write good test I would recommend Practical Object-Oriented Design in Ruby: An Agile Primer by Sandi Metz(which is one of a best book I've read)

like image 112
Kocur4d Avatar answered Nov 15 '22 17:11

Kocur4d