- a *nix system
- Ruby installed -
rvm(curl -sSL https://get.rvm.io | bash) is useful for installing the latest version and keeping gems local to your user - a directory called
workspacein your home directory - Atom editor installed - https://atom.io/
cd ~/workspace/(go to your workspace)mkdir fizzbuzz(make a new directory)cd fizzbuzz(change into it)gem install bundler(install a gem (1) called 'bundler')bundle init(run Bundler (2) program, passing theinitargument - this creates a file calledGemfile)atom Gemfile(open the file in Atom)- Change
gem "rails"togem "rspec"and go back to the command line (this tells Bundler that we want to install a gem called RSpec) bundle install(run Bundler again, this time passing it theinstallargument)bundle exec rspec --init(tell the bundler program to run the rspec program, passing the--initargument to it)mkdir lib(make another directory to hold the actual code)touch lib/fizzbuzz.rb(create a file that we'll use later)atom spec/fizzbuzz_spec.rb(open a file to hold our tests - thespecdirectory was created byrspec --init)
In the file you've just opened, require the fizzbuzz file and start a description for it:
require 'fizzbuzz'
describe FizzBuzz do
endWe are describing something with the name FizzBuzz so that clearly needs to exist.
Open lib/fizzbuzz.rb in Atom e.g. run atom lib/fizzbuzz.rb on the command line and then create the FizzBuzz module:
module FizzBuzz
endAside: by putting the fizzbuzz.rb file into the lib directory, RSpec can find it so we can include it simply with require 'fizzbuzz', rather than having to tell the test file where it is on the file-system with a relative or absolute path e.g. require '../fizzbuzz.rb' or require '/Users/billbums/workspace/fizzbuzz/fizzbuzz.rb'.
At this point we can run RSpec and see the output:
bundle exec rspec
You should see something which looks like this:
No examples found.
Finished in 0.00029 seconds (files took 0.07641 seconds to load)
0 examples, 0 failures
So everything runs, we just haven't written any tests yet.
Next, let's write a test. If it's not already, open up the fizzbuzz_spec.rb file in the spec directory: atom spec/fizzbuzz_spec.rb and add a test to it. We'll test the basic case when we pass 1 to a function called fizzbuzz and the test will look like this:
it "returns 1 for 1" do
expect(FizzBuzz.fizzbuzz(1)).to eq(1)
endIt needs to go inside the describe block, so the whole thing will look like this:
describe FizzBuzz do
it "returns 1 for 1" do
expect(FizzBuzz.fizzbuzz(1)).to eq(1)
end
endOK, so what have we written? Well, we've called the fizzbuzz method on the FizzBuzz module and passed 1 into it: FizzBuzz.fizzbuzz(1).
We've passed that whole thing into a method called expect: expect(FizzBuzz.fizzbuzz(1))
Then we've called to on whatever that returns: expect(FizzBuzz.fizzbuzz(1)).to
And then, finally, we've said that we want that to be equal to 1 by using the eq method: eq(1)
(Notice that we have left the brackets off the to method to make this read more like a sentence, which is a convention that's often used).
We can run the tests again, now that one exists.
Run with: bundle exec rspec
The output should look something like this:
F
Failures:
1) FizzBuzz returns 1 when passed 1
Failure/Error: expect(FizzBuzz.fizzbuzz(1)).to eq(1)
NoMethodError:
undefined method `fizzbuzz' for FizzBuzz:Module
# ./spec/fizzbuzz_spec.rb:5:in `block (2 levels) in <top (required)>'
Finished in 0.00179 seconds (files took 0.09138 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/fizzbuzz_spec.rb:4 # FizzBuzz returns 1 when passed 1
The test has failed because the method we're calling doesn't exist. That's fine - we'd like to see the test fail first before we make it pass.
Test Driven Development (TDD) is a software development practice which involves writing a test first, before the code to make it pass actually exists.
You write the test first, run it to see it fail, write the code to make it pass then run the test again and see that all is well (ideal world).
This has several benefits:
- you know that what you've written definitely satisfies your test
- you can write just enough code to make the tests pass leading to less complexity and full test coverage
- your code has tests (it's much harder to go back and write them retrospectively)
Finally (for this introduction) we can make this test pass.
Adding this simple method inside the FizzBuzz module will make our first test pass (we have to add more tests for the rest of FizzBuzz):
def self.fizzbuzz(n)
n
endRun the tests again, as before, and see your triumph.
Ruby code can be wrapped up into a package so that it can be installed and used in other programs, projects and by other people. These packages of code are called 'Gems'. Reusing code that other people have written is essential if you want to get anything done... See https://www.ruby-lang.org/en/libraries/ We've installed, via a Gem, a program called Bundler. We can give Bundler a list of Gems (in the `Gemfile`) that we want to install and it will manage the installation of all of them. (Imagine you had to install 10 different Gems - running 'gem install ...' 10 times would be tedious and error-prone).Bundler will also make sure that your program can find the Gems to be able to use, plus allow you to run programs installed via Gems e.g. the rspec program.
Bundler also generates a file called Gemfile.lock, which you should checkin to source control (Git). It contains the specific versions of the dependencies which were installed, so if you install RSpec version 1.2.3 and everything works then the next person to follow your instructions should get the same version to avoid any unforseen problems.
The Bundler site is here: http://bundler.io/