Introduction to Ruby and RSpec…
The third week of the prep course is quite challenging and interesting. This is my first time to use use Ruby and Rspec test and I found it very useful to make programming more productive. Well, I will give a brief introduction of Ruby and Rspec before showing what I have done.
Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write. On the other hand, Rspec is a unit test framework for the Ruby programming language. It is indeed a behavior driven development tool since the tests focuses on the “behavior” of the application instead on how it works. It gives emphasis on how the application behaves instead of how it works.
Here are some useful Rspec keywords:
- describe keyword — used to define an “Example Group”. You can think of an “Example Group” as a collection of tests.
- it keyword — is used to define an “Example”. An example is basically a test or a test case.
- expect keyword- The expect keyword is used to define an “Expectation” in RSpec. This is a verification step where we check, that a specific expected condition has been met.
Simulating Fizzbuzz game
Fizzbuzz is a group word game for children to teach them about division. It’s a nice game for kids that will help them familiarize division. The rules are so simple, but force players to quickly analyze a number in several different ways.
The rules:
- If the number is divisible by 3, the person doesn’t say the number, instead, they say “Fizz!”.
- Also, if the number contains a 5, the player must say “Buzz!”.
- Similarly, if the number is divisible by 3 and 5 a they must say “FizzBuzz!”.
- These fizzes and buzzes combine together — for example, instead of 35, I would say “Fizz, fizz, buzz!”, because the number contains a 5, is divisible by 5, and is divisible by 7.
Fizzbuzz game was written in different programming languages since it is a good exercise for beginners. Obviously we will make a program that will simulate the fizzbuzz game using Ruby and Rspec.
Write a test and program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.”
Now, let’s do it!
- Start by creating a new directory: fizzbuzz
2. Open the fizzbuzz directory and create the Gemfile
3. Open the Gemfile and add rspec as a dependency:
4. Now, we have to install the gem by typing $ gem install bundler
5. Then run bundle install
in your terminal
6. Now, initialize RSpec by typing $ rspec --init
in the command line.
7. Open the .rspec
file (it is a hidden file) and change it's content to display the tests results in a verbose format.
# .rspec
--format documentation
--color
--require spec_helper
8. To check if you are on the right track, go back to your terminal and run the rspec
command and check if you have the same output with the figure below.
Basically, if you got the same output it means that you have set up RSpec and ready to write your first test.
Experience first test with fizzbuzz:
- First, we need to create a test file. You have to remember that all our specs will live in the
spec
folder. Now, create a file and name it with:fizz_buzz_spec.rb
Afterwards, open it up in your editor and add the following code:
require './lib/fizz_buzz' #will look for and test the source file in
#the lib folder, called fizz_buzz.rbdescribe 'fizz_buzz' doend
Unfortunately, at this moment it will not find that file since we have not created it yet). Therefore, our test will give us an error.
Well, do not be afraid for the long list of errors, just focus on the first two lines since these are the most important lines, in this case we have:
Quite often, RSpec is returning a lot of text when we get an error and it can get quite intimidating. One tip is to always look at the top of the output. That is where the important message is.
Since RSpec is telling us that there is no file in the lib
folder called fizz_buzz.rb
. Since we do not have it, let's create one.
$ mkdir lib
$ touch lib/fizz_buzz.rb
Now, let us check what you have created so far. It is important that we agree on three things at this point.
- Your tests/specs are placed in the
spec
folder - Your implementation (or production) files are placed in the
lib
folder - Your settings (like
Gemfile
, etc.) are placed in the main project folder
Let’s make some decisions. Let’s say that we want a main method in our program that can take a number and return ‘fizz’, ‘buzz’, fizzbuzz’ or the number if no conditions are met. That is our objective, right?
Add the following it
block to your test file (make sure to place it inside the describe
block)
# spec/fizz_buzz_spec.rbit 'returns 1 if number is 1' do
expect(fizz_buzz(1)).to eq 1
end
Go back to your terminal and run RSpec. You will get an error:
Again, a lot of text, but if you read the message carefully, you’ll see that we are getting a NoMethodError
. This means our test can't find a method called fizz_buzz
. Again, we haven't created it yet - so that's what we would expect.
Even though we know our test will fail, we still want to run it and see our expected error. This way we will be confident that we are ok to go on. If we get the wrong error, something else is broken.
Let’s write the minimum amount of code to make this test pass:
# lib/fizz_buzz.rbdef fizz_buzz(number)
number
end
This very simple method takes a number as an argument and simply returns it. In Ruby, methods return their last line. They output the last thing before the end
. Here, our method will return
our number
.
It makes the test pass — try by running the rspec
command in your terminal again.
So we asked the program to return ‘1’ if we pass in ‘1’. All in accordance with the game rules, right? We’ve written the minimum amount of code needed to make that happen. At this stage we are all good.
But we are far from done, right? One of the objectives is to return the word fizz
if the number we pass in is divisible by 3. Let's write a test for that.
# spec/fizz_buzz_spec.rbit "returns 'fizz' if number is divisible by 3" do
expect(fizz_buzz(3)).to eq 'fizz'
end
If you run rspec
again, you'll get an error (of course)
Let’s say that we want our implementation to have a some methods that checks if a number is divisible by 3, 5 and 15.
Let’s make use of flow control. We want our program to go down the ‘fizz’ path only if number
is evenly divisible by 3.
# lib/fizz_buzz.rbdef fizz_buzz(number)
if number % 3 == 0
'fizz'
end
end
If modulus (%) is still unclear, try this YouTube video explaining it visually. Modulus is an important, though confusing, operator to understand.
Remember that we can always open up irb and play around. You can copy the method above and see what different numbers will give you fizz
. You'll notice that only numbers that evenly divide by 3 will return 'fizz' - 3, 6, 9, etc. You'll have to write fizz_buzz(3)
or fizz_buzz(9)
to see fizz
. Try writing just fizz_buzz
. Why are you getting that error?
Now, this code will make our second test to pass. Try it.
Since we are asked to continue the testing, I come up with the implementation methods with different algorithms that shows the simulation of the fizzbuzz game:
First implementation method, continuation of the lesson:
My test file:
my implementation method#1:
The result in the rspec:
Tests in irb:
Test File #2:
Here, I put more test blocks to test the implementation method.
My Implementation Method#2:
Sample run:
Extra Credit: The Sad Path
What we wrote so far is called the “Happy Path”. It’s the actions we expect a “normal” user to take. A “normal” user would put in 3 or 12 or 13987.
But what if the user puts in “dinosaur”?
Solution:
The loop will check until the user input is an integer (Fixnum)
Sample Run:
The program will ask the user to enter a number if they type a string like ‘dinosaur’.
References: