Basic introduction to Ruby Minitest - ruby

I am using minitest for the first time and I am having trouble understanding how to write my first test method. Can anyone assist in helping me understand what I should be testing in the below Player method get_name?
class Player
def get_name(player)
puts `clear`
center("#{player}, whats your name bro/ladybro?")
#name = gets.chomp
until #name =~ /\A[[:alnum:]]+\z/
center("you can do a combination of alphanumeric characters")
#name = gets.chomp
end
end
end
This is what I have in my test file, I was thinking I am just suppose to test the regex to make sure it takes alpha and numeric characters.
class TestPlayer < Minitest::Test
def test_get_name
describe "get_name" do
it "should allow an input of alphanumeric characters" do
assert_match(/\A[[:alnum:]]+\z/, "test_string123")
end
end
end
end
but when I run the tests, nothing seems to happen, I would imagine I am suppose to have 1 assertion.
Run options: --seed 10135
# Running:
.
Finished in 0.001565s, 638.9776 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 0 errors, 0 skips
can anyone assist in demonstrating how I should write a test for this scenario? thanks.

Minitest test may be described as follows (Assertion syntax):
Its just a simple Ruby file which has a class thats typically a subclass of Minitest::Test.
The method setup will be called at the first; you can define objects that you may require in every test. Eg: Consider assigning an instance of the Player object here in an instance variable in setup method so that you can use it elsewhere in the test class.
A test is defined inside a method that starts with the string: test_; any other method can be used to reduce duplication of code but it won't be considered part of tests.
Typically you should think about testing the return value of a method you want to test.
Testing a method with external input is more convoluted, I would suggest to start with testing methods that have testable output.

Related

RSpec: Using a method stub to test interactive user input

I'm new to both Ruby and RSpec, and have already spent hours trying to write the first steps for testing an interactive tic tac toe program, but haven't located any useful answers to the error I'm getting:
> bundle exec rspec --format documentation
Do you want to play tic-tac-toe? (y/n)
An error occurred while loading ./spec/tic_tac_toe_spec.rb.
Failure/Error: choice = gets.chomp.downcase
Errno::ENOENT:
No such file or directory # rb_sysopen - --format
# ./lib/tic_tac_toe.rb:70:in `gets'
# ./lib/tic_tac_toe.rb:70:in `gets'
# ./lib/tic_tac_toe.rb:70:in `start_game'
# ./lib/tic_tac_toe.rb:144:in `<top (required)>'
# ./spec/tic_tac_toe_spec.rb:1:in `require'
# ./spec/tic_tac_toe_spec.rb:1:in `<top (required)>'
No examples found.
Finished in 0.0004 seconds (files took 0.08415 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples
When you start the game, it prompts whether you want to play (y/n). I've already attempted different variations of using mocking and stubs but always get this same error. I imagine I'm missing something very basic but cannot see it.
Question: how would one adequately address (using RSpec) this first prompt of the game?
The game uses an independent method (start_game) but not a class to start. I'm looking for some way mock or stub this with some default input (like y or n) as I want the tests to be automated. All the examples I see use a class to start a program, however (rather than a stand-alone method like here).
Currently in spec/tic_tac_toe_spec.rb there are some let keywords for :output and :game which I found from an example somewhere but obviously this doesn't work.
Edit I want to test the following method. The RSpec code keeps choking on the choice = gets.chomp.downcase line.
def start_game
puts "Do you want to play tic-tac-toe? (y/n)"
choice = gets.chomp.downcase
unless ["y","n"].include?(choice)
abort("Please answer with y or n.")
end
case choice
when "y"
puts "Ok, let's start!"
b = Board.new
puts "Enter name of player 1 (O)"
player1name = gets.chomp
player1 = Player.new(player1name)
puts "Now enter name of player 2 (X)"
player2name = gets.chomp
player2 = Player.new(player2name)
play_game(player1, player2, b)
when "n"
puts "Your loss."
end
end #start_game
You are receiving the error because you are passing the --format documentation as a parameter to RSpec.
gets reads the ARGV that have been passed. This also includes the RSpec parameters. You should use STDIN.gets so that you only read standard input and not the parameters.
You can read more about in this question:
https://stackoverflow.com/a/19799223/6156030
A simple approach could be:
it 'works' do
allow($stdin).to receive(:gets).and_return('y')
expect { start_game } # (Or however you run it!)
.to output("Ok, let's start!")
.to_stdout
end
You can also specify multiple return values for gets, which will be used sequentially as the test runs.
The alternative approach you seem to have started (but not fully implemented) is to inject an explicit output (and, presumably, one day an input) object into the game.
This would indeed be a cleaner approach from the purist's point of view - i.e. not stubbing the global objects like $stdin - but is probably overkill for your initial version of the code. Unless you plan doing something fancy like running parallel specs, I wouldn't worry about that.
Edit: After looking at your actual code in more detail, I see the problem. You're defining global methods which are doing multiple things and are tightly coupled. This makes writing tests much harder!
Here I have added a working test example:
https://github.com/tom-lord/tic_tac_toe_rspec/commit/840df0b7f1380296db97feff0cd3ca995c5c6ee3
However, going forward in order to simplify this my advice would be to define all each method within an appropriate class, and make the code less procedural. (i.e. Don't just make the end of one method call the next method, in a long sequence!) This refactoring is perhaps beyond the scope of a StackOverflow answer though.
You need to stub and mock gets method in your specs:
yes_gets = double("yes_gets")
allow($stdin).to receive(:gets).and_return(yes_gets)
Which then you can make it respond to #chomp:
expect(yes_gets).to receive(:chomp).and_return('Y')
You can cover the similar method call for downcase by returning this double object itself.
You can also do the similar work for mock object for your 'N' case where you'd expect game to exit when player inputs an N(No):
no_gets = double("no_gets")
allow($stdin).to receive(:gets).and_return(no_gets)
expect(no_gets).to receive(:chomp).and_return('N')

basic undefined method for #<RSpec::ExampleGroups:::0x00000101906440>

I started and to code back in 2013 and took a break from it and decided to start from scratch. But for the life of me cannot get pass this error. I've done everything I can think of, but it's not requiring the methods from the lib file.
here is my folder structure
fizzbuzz
lib/
fizzbuzz.rb
spec/
fizzbuzz_spec.rb
spec_helper.rb
.rspec
fizzbuzz_spec.rb file
require "fizzbuzz"
describe "fizzbuzz" do
it "tells me that 3 is divisible by 3" do
expect(divisible_by_three?(3)).to_eq true
end
end
Heres the fizzbuzz.rb file
def fizzbuzz
def divisible_by_three? (number)
number % 3 ==0
end
end
Yes I understand it's not the most puzzling thing in the world. But my mind s a blank and I've troubledshoot this and searched online and followed tutorials to do it another way, but it never seems to call the method.
I've used
require "fizzbuzz"
require './lib/fizzbuzz'
require_relative '../lib/fizzbuzz'
Please help, please and thank yous.
Kind regards,
Grateful developer.
There are 2 problems here as I see it. In the lib/fizzbuzz.rb file, your method divisible_by_three? method is enclosed in another method. While this is technically allowed, it's not really advisable if it can be avoided. Instead, change def fizzbuzz to class Fizzbuzz.
class Fizzbuzz
def divisible_by_three? (number)
number % 3 ==0
end
end
In the spec file, require_relative '../lib/fizzbuzz' is fine. However, to use the method (at least as defined in the previous snippet), you'll need to use an instance of the class to have access to the method. See the snippet below.
require_relative '../lib/fizzbuzz'
describe "fizzbuzz" do
let(:instance) { Fizzbuzz.new }
it "tells me that 3 is divisible by 3" do
expect(instance.divisible_by_three?(3)).to eq true
end
end

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.

RSpec test for a module

I'm brand new to RSpec and TDD. I was wondering if someone might help me with creating a test well-suited for this Module:
module Kernel
# define new 'puts' which which appends "This will be appended!" to all puts output
def puts_with_append *args
puts_without_append args.map{|a| a + "This will be appended!"}
end
# back up name of old puts
alias_method :puts_without_append, :puts
# now set our version as new puts
alias_method :puts, :puts_with_append
end
I'd like for my test to check that the content from a 'puts' ends with "This will be appended!". Would that be a sufficient test? How would I do that?
The best tests test what you're trying to achieve, not how you achieve it... Tying tests to implementation makes your tests brittle.
So, what you're trying to achieve with this method is a change to "puts" whenever your extension is loaded. Testing the method puts_with_append doesn't achieve this goal... If you later accidentally re-alias that to something else, your desired puts change won't work.
However, testing this without using an implementation detail would be rather difficult, so instead, we can try to push the implementation details down to somewhere they won't change, like STDOUT.
Just the Test Content
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
puts "One"
Full Test
I'm going to turn this into a blog post within the next day or so, but I think you should also consider that you've got a desired result for one and many arguments, and your tests should be easy to read. The ultimate structure I'd use is:
require "rspec"
require "./your_extention.rb"
describe Kernel do
describe "#puts (overridden)" do
context "with one argument" do
it "should append the appropriate string" do
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
puts "One"
end
end
context "with more then one argument" do
it "should append the appropriate string to every arg" do
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
$stdout.should_receive(:write).with("TwoThis will be appended!")
puts("One", "Two")
end
end
end
end

Generating many almost identical ruby unit tests

I have a number of ruby files (a.rb, b.rb, c.rb) which define very similar classes. (They should test the same)
I've written a unit test to test these subclasses and I've generated contexts for each of the classes programatically (see below) — should I instead programatically create entire Test Classes instead? If so why and how?
I'm using the shoulda unit test extensions so my files look something like this:
a.rb
class ItsA
def number
1123
end
end
b.rb
class ItsB
def number
6784
end
end
test_letters.rb
require 'rubygems'
require 'test/unit'
require 'shoulda'
class LettersTest < Test::Unit::TestCase
Dir.glob('letters/*.rb') do |letter|
context "The #{letter} letter file"
setup do
# Here I require the ruby file and allocate
# #theclass to be an instance of the class in the file.
# I'm actually testing JavaScript using Harmony, but
# putting those details in might complicate my question.
end
should "return a number" do
assert #theclass.number.is_a? Number
end
end
end
This does the job reasonably well, but should I do some other jiggerypokery and create LetterATest, LetterBTest etc. automatically instead? If so, how would you go about doing it and why?
This really depends on how similar your classes are, but assuming they're pretty much identical and require a few small tests, and you're using shoulda, you could do something like:
class LettersTest < Test::Unit::TestCase
context "The letter classes"
setup do
#instances = # your code to get a list of the instances
end
should "return a number" do
#instances.each do |instance|
assert instance.number.is_a?(Number), "#{instance.class}.number should be a number"
end
end
end
end
In our codebase we've found that a large number of auto-generated contexts and tests leads to pretty slow test execution, so we favor the approach above to minimize the number of contexts/tests. You may not have this issue.

Resources