16

This has always bothered me. Why is it people say to unit test in rspec but integration test in cucumber? I am not asking why these tests are necessary - I know what the difference is between integration and unit testing. I just don't see why, given cucumber's completely customizable syntax, it isn't used for unit testing?

It seems to me like the same amount of code is written for cucumber and rspec, the only difference is that for cucumber you separate the test logic from the test writing.

JW8
  • 1,496
  • 5
  • 21
  • 36
ryeguy
  • 65,519
  • 58
  • 198
  • 260
  • 2
    JBehave 1.0 came first - originally with separate mechanisms for class examples/tests and scenarios. This was the time of JUnit 3.8 - JUnit 4.4 made the class example stuff unecessary. Dan North (BDD and JBehave founder) developed RBehave, then the RSpec team integrated it, then David Chelimsky made it use plain text and they split it back out into a separate library - I think Aslak Hellesoy led this. We then rewrote JBehave 2.0 based on Cucumber. For historic interest: http://blog.davidchelimsky.net/2007/10/25/plain-text-stories-part-iii/ – Lunivore Nov 17 '10 at 23:52
  • Oh, and Cucumber's a little bit slower to write. See my answer for the reason why and the problems the frameworks solve. – Lunivore Nov 17 '10 at 23:57

5 Answers5

10

There is quite a lot of overhead in using cucumber for unit testing. Not only you have to write the features but then map them to the implementation using a separate bit of code.

Unit testing is meant to be very fast to write and very fast to execute. Naturally, cucumber focuses on end user experience, mainly due to the language used in writing a feature.

Just to refresh, a feature would contain the following:

As some stakeholder of a system
I would like to perform an activity
So that I can get some benefit out of it

Given a precondition
When I perform an action
Then something should happen

The opening paragraph, that is often ignored, is very important as it sets a context for the operation and explains why something happens. Because of the use of natural language, these things are easy to show to non-programmers in order to get some feedback.

Now, using these for unit tests would seem awkward at best. First of all, the end user focus suggests a more integration approach as the feature says nothing about mocks and UI/logic separation. I.e. a feature like the following just seems weird:

Given a that a database mock is configured with the following data
| ID  | Username |
| 0   | igor     |
When call FindAll on User Repository
Then I get the following user back
| ID  | Username |
| 0   | igor     |

Also, as your SUT gets smaller (i.e. a class), the context of operation is not as important. A User repository does not care about the context e.g. it doesn't care if the consumer of it is a normal user or a VIP user. A simple component (which it should be, following SRP), is completely deterministic based on its inputs.

So, the unit test is there to validate that what you wrote is correct, and the cucmber test is there to validate that what you wrote satisfies some higher purpose by putting a behavior of the system in a context.

Igor Zevaka
  • 74,528
  • 26
  • 112
  • 128
  • What about reusing testing code by writing step definitions? Does not that matter? – inf3rno May 23 '15 at 04:47
  • 1
    I could imagine something like this: `Feature users are persisted by the user repository Scenario finding users with the repository Given that the storage contains only user igor When I ask the repository to find all users Then I get only user igor back` - this is more abstract, but it is hard to map these terms to the UserRepository.FindAll and to the MockDatabase – inf3rno May 23 '15 at 04:53
  • Interesting. I'm starting to realize the SUT should not be the class for my unit tests, and am wondering if just because of Cucumber's overhead and the motivation it provides to focus on behavior as a 'unit' it is actually better as a unit testing framework than plain ol junit. – cogitoboy Aug 06 '23 at 22:49
5

Cucumber solves a particular set of problems - engaging business stakeholders who can't easily read code and certainly can't write it, and providing reuse between the steps in automated scenarios. The scenarios also usually cover more than one aspect of behaviour, documenting the functionality of the entire system and often covering whole user journeys across multiple components. The step-based architecture which Cucumber encourages is ideal for handling these scenarios.

It also introduces a whole set of other problems. First, you need to tie the Cucumber scenarios to a set of fixtures, so there's another layer of abstraction which makes them slower to write. Second, English is harder to refactor than code - even with dynamic languages like Ruby (the difference is still more pronounced in the C# and Java variants like JBehave, SpecFlow, Cuke4Nuke and Cuke4Duke). It's harder to tell if steps are still being used, and harder to maintain the scenarios. It's also harder to manage state between the various steps.

With unit tests, the audience is technical. Classes ideally have single responsibilities with little to no duplication, so the reuse of steps isn't important. When we want to change an element of code we tend to look for tests whose naming conventions match the files or classes, so a one-to-one mapping with these is ideal.

Because of Cucumber's overheads, and because we don't get value from the benefits Cucumber provides in return for its overheads, RSpec is a better fit for behaviour at a unit level. (This is also true of JUnit, NUnit, etc.)

If you're missing the "Given, When, Then" of Cucumber, try adding them as comments. This works well for me.

Lunivore
  • 17,277
  • 4
  • 47
  • 92
1

The general idea is that Cucumber tests are written at a higher level than traditional unit tests. For instance when you unit test a particular module you're focused on testing only that modules functionality in isolation away from the rest of the system. The interface into other parts of the system should generally be represented with mock objects.

Cucumber on the other hand focuses on system testing from the UI all the way through the data persistence layer.

Unit testing = Mechanical Engineer testing his new engine in a lab setting mounted on a harness.

Cucumber testing = Test driver putting it on a track for a spin.

danielz
  • 11
  • 1
  • But that's the problem. Nothing you said is specific to rspec, and there is nothing that cucumber can't do. Underneath the scenarios, cucumber is just regular ruby code, which can include mocks and class-level testing. So why isn't it? – ryeguy Nov 17 '10 at 15:27
  • Sure, you're right it's just code underneath it all. Where there is code there is the possibility of doing whatever you want given enough persistence. However, that model of testing isn't the intent of Cucumber and would defeat the purpose of full stack system testing if you were to start mocking objects. Sounds like you're wanting to use a screwdriver to drive nails. – danielz Nov 17 '10 at 17:35
  • 1
    You still aren't being specific enough. Why **exactly** is rspec more suitable for this? Just because I'm doing 2 different style of testing doesn't mean I must use 2 different frameworks. There are plenty of decoupled mocking frameworks out there that can easily be used from within cucumber. With both rspec and cucumber you write descriptive test names, so how is it different? When it comes down to it, the difference is cucumber's scenarios are in a separate file. Don't just say "well nothing is stopping you". I know that. I just want to know why everyone thinks its so wrong. – ryeguy Nov 17 '10 at 18:58
0

According to Aslak Hellesøy, Cucumber is not a testing tool, rather a collaboration tool (for BDD). He recently posted about this apparent misconception:

https://cucumber.pro/blog/2014/03/03/the-worlds-most-misunderstood-collaboration-tool.html

That said, there are a lot of tests written below the acceptance level that would fall around the integration test layer. Ie,

Given a user at 123 Evergreen Terrace
When I lookup their name
Then I get Homer Simpson

instead of

Given an address
When I lookup a name
Then the home owner name is displayed

It doesn't help that the cukes.info shows as an imperative example (rather than declarative).

Cucumber tests run slow compared to Rspec or Test::Unit or MiniTest unit tests. They have a lot of overhead (it can take minutes to load up the environment, including all the Page Object classes, parse the feature files, and actually execute tests). Running just Cucumber unit tests would be faster than running integration equivalent tests, but not as fast as running something more light weight such as the three above mentioned.

(I am responding to this old question since it was one of the first hits I got when searching for alternatives to Cucumber for integration testing)

mrcallahat
  • 21
  • 1
0

Unit tests are to test a particular unit of code in isolation. The usual granularity being a method (or few interacting methods) in a class.

Integration tests are focused on testing many layers of the application stack. For example, you can run an integration test that checks the interaction of you code with a database. Most integration tests focus of two or three layers of objects.

Cucumber tests in particular tend to focus on the full application stack, as they exercise the application by simulating a real user in the interface and all layers of the application, from the UI to the back end services (databases, file system, networks, etc) are in place and used.

Bottom line:

a unit test checks that a particular piece of code abides to its contract with the rest of the world...

Whereas a cucumber test checks the interaction of several pieces of code (a vertical slice some people may say), with the additional beauty that the test itself reads like plain English.

carlosayam
  • 1,396
  • 1
  • 10
  • 15