Ruby: Minitest/spec and BDD Gherkin - ruby

It seems that I cannot find much documentation on Minitest/spec so I was wondering if somebody could help me figure out how to do what I need to do. Basically I want to run tests on all my classes and modules to make sure they 1.) Output the right value type if static or 2.) In the case of to_symbols all keys are symbols. Here is what I tried so far:
What I assume the Gherkin would look like:
Given binns
When the version method is called
then the return should be a float
and version should be a method or constant
What I assumed it would be with minitest/spec:
require 'minitest/autorun'
require 'minitest/spec'
require 'binns'
given Binns do
when "the version method is called" do
then "the return should be a float" do
# Do work
end
end
end
But I get:
syntax error, unexpected keyword_when (SyntaxError)
when "the version method is called" do
^
Note: I am also open to other suggestions for testing (I don't know much about cucumber and heard it was hefty) or if somebody has a book suggestion, please do tell I've been looking for a good book on Ruby Unit Testing.

MiniTest/Spec uses Rspec-style syntax: desc, it, before, after...
require 'minitest/autorun'
require 'minitest/spec'
require 'binns'
desc Binns do
it "should return a float when the version method is called" do
# Do work
end
end
If it'd make the transition easier for you, you could alias the "desc" method to "given" and "it" method to "when".
Good tutorial from Peter Cooper here.

Related

First time unit testing with rspec in ruby

I am just trying to understand the basics behind unit testing. I wrote a Player class in a file called Player.rb. Here is the code:
Class Player
attr_reader :health
attr_accessor :name
def initialize(name, health=100)
#name = name.capitalize
#health = health
end
def blam
#health -= 10
puts "#{#name} just got blammed yo."
end
def w00t
#health += 15
puts "#{#name} just got w00ted."
end
def score
#health + #name.length
end
def name=(new_name)
#name = new_name.capitalize
end
def to_s
puts "I'm #{#name} with a health of #{#health} and a score of #{score}"
end
end
Here is my spec file:
require_relative 'Player'
describe Player do
it "has a capitalized name" do
player = Player.new("larry", 150)
player.name.should == "Larry"
end
end
Does that seem about right? My question is in regards to the syntax of the spec file. I understand why I need to require the Player class. But what is the describe and it sections of the code doing? Why do I need the it section? All it seems to be doing is defining a string right?
Finally, when I run rspec player_spec.rb from Terminal, I get this warning:
Deprecation Warnings:
Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` instead. Called from /Users/Jwan/studio_game/player_spec.rb:7:in `block (2 levels) in <top (required)>'.
What does the above warning mean? Do I have to replace should with enable syntax? How do I enable the :should syntax? Why is :should written as a symbol?
Yes, that seems about right. One thing you might find useful is to use subject, which lets you define a "subject" to use in multiple tests:
describe Player do
subject { Player.new("larry", 150) }
it "has a capitalized name" do
expect(subject.name).to eq "Larry"
end
end
This way you don't have to define player over and over again in every test—subject will automatically initialize it for you each time.
describe and it are primarily for organization. A large project will ultimately have thousands of tests, and a change in one class might cause a test to fail for a completely different part of application. Keeping tests organized makes it much easier to find and fix errors as they occur.
As for your warning, it looks like you're using an old guide or tutorial that tells you to use "should" syntax, but in RSpec 3 this syntax is deprecated and "expect" syntax is required instead. You can see how I changed your code above to use "expect" syntax. Here's a good blog post on the new syntax (and why the old syntax is deprecated).
it sets up an actual example. An example can be thought of as an actual test which contains one or more expectations (best practice says one expectation per example).
The expect method and matchers only exist in the it block. Variables set up with let and subject are unique for each example.
scenario is an alias for it used in feature (acceptance) specs.
describe, context and feature are used to group examples together. This provides both readability and encapsulation for let and subject variables.
Passing a class to describe also sets up an implicit subject:
RSpec.describe Array do
describe "when first created" do
it { is_expected.to be_empty }
end
end
RSpec has relativly recently undergone a large shift towards eliminating "monkey patching" core ruby classes which depreciated the should syntax.
While it is recommended to use expect for new projects, you can allow should and other monkey patching methods.

How to start tdding with ruby and wrong?

I know how to do TDD in other languages, but I'm new to both ruby and wrong. I'm struggling a bit with the fundamentals of how to setup a (toy) project. I want to write a method which computes the factorial (n! = 1 * 2 * 3 * ... * n). I have created the file test/factorial_test.rb, which so far contains
require 'wrong'
include Wrong
How do I proceed from here? Do I write my assertions in the global scope of the file
assert { factorial(1) == 1 }
assert { factorial(2) == 2 }
#...
(which feels a bit weird)? Or should I follow some (which?) convention and wrap each test in its own method
def one_factorial_should_be_one
assert { factorial(1) == 1 }
end
I'm a bit lost with the fundamentals here, so any answer on what is considered best-practice here is highly appreciated.
Wrong merely provides a couple (admittedly smart) assertion methods. You still need a framework to automate running the tests, e.g. minitest.
You can start with minitest testing framework that included with standard library. it is more productive and simple then using assert directly.
There are several testing frameworks available in Ruby
Test::Unit
minitest
rspec
shoulda - similar to rspec
cucumber - a BDD testing framework
The basic Ruby testing framework is Test::Unit. A simple example looks as follows:
require 'test/unit'
class MyTest < Test::Unit::TestCase
def test_equality
assert_equal(1,1)
end
end
Recently Minitest was incorporated into Ruby standard library, so if you wish to use this library you don't have to install anything. A simple minitest spec looks as follows:
require 'minitest/autorun'
describe Factorial do
it "should provide factorial of 1 as 1" do
factorial(1).must_equal 1
end
end

Is there something similar to Nokogiri for parsing Ruby code?

Nokogiri is awesome. I can do things like #css('.bla') which will return the first matching element.
Right now we need to do some parsing of Ruby source code - finding all methods within a class etc. We're using the ruby_parser gem, but all it does is comb your source code and spit out S-expressions. Is there anything like Nokogiri for these S-expressions which can do things like "return S-expression for first method found named 'foo'"?
The only thing I can think of, is Adam Sanderson's SExpPath library.
Although I am accepting Jörg's answer because it is more complete, I ended up discovering something else which I ended up using. ruby_parser installs a dependent gem named sexp_processor (it is in this gem where the Sexp class is actually defined). If you view the class docs there are a few methods that will help with basic Ruby finders. Here's an example:
class Sexp
def name # convenience method
self.sexp_body.first
end
end
# Print out all instance methods within classes. Beware - if "code" sexp itself
# is a class, it will NOT be included!
code = RubyParser.new.parse(IO.read('/src/file'))
code.each_of_type(:class){ |klass|
klass.each_of_type(:defn){ |meth|
puts meth.name
}
}
I don't know anything about gem that you are looking for, but you can find all methods within a class using instance_methods:
class Foo
def bar
end
end
irb(main):005:0> Foo.instance_methods - Object.instance_methods
=> [:bar]
You could check the rubinius parser, it may help you to do what you want.

Ruby Koan: test_nil_is_an_object

I have recently tried sharpening my rails skills with this tool:
http://github.com/edgecase/ruby_koans
but I am having trouble passing some tests. Also I am not sure if I'm doing some things correctly since the objective is just to pass the test, there are a lot of ways in passing it and I may be doing something that isn't up to standards.
Is there a way to confirm if I'm doing things right?
a specific example:
in about_nil,
def test_nil_is_an_object
assert_equal __, nil.is_a?(Object), "Unlike NULL in other languages"
end
so is it telling me to check if that second clause is equal to an object(so i can say nil is an object) or just put assert_equal true, nil.is_a?(Object) because the statement is true?
and the next test:
def test_you_dont_get_null_pointer_errors_when_calling_methods_on_nil
# What happens when you call a method that doesn't exist. The
# following begin/rescue/end code block captures the exception and
# make some assertions about it.
begin
nil.some_method_nil_doesnt_know_about
rescue Exception => ex
# What exception has been caught?
assert_equal __, ex.class
# What message was attached to the exception?
# (HINT: replace __ with part of the error message.)
assert_match(/__/, ex.message)
end
end
Im guessing I should put a "No method error" string in the assert_match, but what about the assert_equal?
assert_equal true, nil.is_a?(Object) is indeed the correct solution. The question is "Are nils in Ruby objects or not?", and in Ruby's case, they are. Thus, in order to pass the assertion, you should assert the truth of that test.
In the second example, when you call an undefined method on nil, you get NoMethodError: undefined method 'foo' for nil:NilClass. Thus, the exception class is NoMethodError, and the message is undefined method 'foo' for nil:NilClass. Test the failing behavior in a console, and see what you get from it, and then apply that knowledge to the test.
Are you running
ruby path_to_enlightenment.rb
at the command prompt after you correct each test? It will give you lots of help.
Also "remember that silence is sometimes the best answer" -- if you are stumped don't put in anything and the tool will help you.
Well, in holding with the typical TDD motto of Red-Green-Refactor, you should run the test (probably with rake in a separate console) and see the failure happen. From there, they have provided you a few pieces of information about what was expected.
As for style, the koans aren't really teaching that. You should just find and read some code written in ruby to get a feel for the typical conventions and idioms of the ruby community.
Simplicity is the key with Ruby Koans - when I started it I thought it must be harder than what it is, but it's not! Just ask IRB the question Koans is asking you, and after a few you get a feel for it. I've written a blog piece about it to help others, too:
Ruby Koans Answers
I remember when I did this that I tried to out think the test and tried to put in
<Answer> and <"Answer">
The thing to remember is that the actual class doesn't have to be in a string or something.
So the answer is NOT
ex.class, ex.class
As suggested above, put the code into irb and execute it.
(1..5).class == Range
is a big hint

Why don't modules always honor 'require' in ruby?

(sorry I should have been clearer with the code the first time I posted this. Hope this makes sense)
File "size_specification.rb"
class SizeSpecification
def fits?
end
end
File "some_module.rb"
require 'size_specification'
module SomeModule
def self.sizes
YAML.load_file(File.dirname(__FILE__) + '/size_specification_data.yml')
end
end
File "size_specification_data.yml
---
- !ruby/object:SizeSpecification
height: 250
width: 300
Then when I call
SomeModule.sizes.first.fits?
I get an exception because "sizes" are Object's not SizeSpecification's so they don't have a "fits" function.
Are your settings and ruby installation ok? I created those 3 files and wrote what follows in "test.rb"
require 'yaml'
require "some_module"
SomeModule.sizes.first.fits?
Then I ran it.
$ ruby --version
ruby 1.8.6 (2008-06-20 patchlevel 230) [i486-linux]
$ ruby -w test.rb
$
No errors!
On second reading I'm a little confused, you seem to want to mix the class into module, which is porbably not so advisable. Also is the YAML supposed to load an array of the SizeSpecifications?
It appears to be that you're not mixing the Module into your class. If I run the test in irb then the require throws a LoadError. So I assume you've put two files together, if not dump it.
Normally you'd write the functionality in the module, then mix that into the class. so you may modify your code like this:
class SizeSpecification
include SomeModule
def fits?
end
end
Which will allow you to then say:
SizeSpecification::SomeModule.sizes
I think you should also be able to say:
SizeSpecification.sizes
However that requires you to take the self off the prefix of the sizes method definition.
Does that help?
The question code got me a little confused.
In general with Ruby, if that happens it's a good sign that I am trying to do things the wrong way.
It might be better to ask a question related to your actual intended outcome, rather than the specifics of a particular 'attack' on your problem. They we can say 'nonono, don't do that, do THIS' or 'ahhhhh, now I understand what you wanna do'

Resources