Generating many almost identical ruby unit tests - ruby

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.

Related

How to test directory reading and addressing method in ruby?

I have a ruby class to read directory and do something for each file. But I am not sure how to test it.
class Scanner
def scan(dir)
Dir.glob(File.join(dir, '**', '*')).each do |path|
verify(path)
end
end
private
def verify(path)
do something for the file...
end
end
I want to have a unit-testing for scan method.
Should I mock something?
Should I provide a test directory in spec/fixtures?
What's the expectation?
Cheers
There are few ways to approach that. One (proposed in the comments) is to separate the concerns, e.g.
class Scanner
def scan(dir)
Dir.glob(File.join(dir, '**', '*')).each do |path|
verify(path)
end
end
private
def verify(path)
Veryfier.new.verify(File.read(path))
# you can test veryfier easily by passing different strings into `verify` method
end
end
And go on with your life, as Scanner#scan is not that complicated anymore, and one would not expect many bugs in there.
But, if for whatever reason, you want it tested as well, you can do one of the following:
set up a dir structure under test/features and tread different directories like different cases. This has one drawback: you have to inspect directories plus read the tests to understand the tests. But it's simple to do.
use Dir.tmpdir and File.tmpfile to set up the dir structure inside your tests. This is a bit more involved, but on the plus side you have the structure and the tests for behavior of the Scanner in a single test file.

Basic introduction to Ruby Minitest

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.

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 can I run MiniTest tests before running normal code in the file?

I want to change my following code
load "API.rb"
require 'minitest/spec'
require 'minitest/autorun'
class Test < MiniTest::Unit::TestCase
MiniTest::Assertions.diff = nil
def setup
end
def test_character_detection
assert_equal "Gandalf", Story.get_first_character(2717)
end
end
puts "TEST"
so tests will run before normal code (in this case - 'puts "TEST"').
Sorry for late answer. Minitests are artful.
minitest/autorun uses at_exit method. It executes the given block when interpreter's work almost done. Roughly speaking "at the end" of your program's lifetime. Though you can call at_exit method more than once and blocks will execute in LIFO order.
More interesting details about at_exit and its usage you can find here.
Also you may look at minitests source code. (in this file at_exit called)
Good luck!

Get list of classes and methods that call a specific global method

I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:
class A
def foo
$muffinize(1)
end
def bar
...
end
end
class B
def shoop
$muffinize(2)
end
def woop
...
end
end
class C
def nope
...
end
end
I would like to the result to be (written to a file):
A:foo
B:shoop
I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?
Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.
If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.
Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either
#!/usr/local/env ruby
require 'ripper'
#require 'pry'
contents = File.open('example.rb').read
code = Ripper.lex(contents)
code.each do |line|
if(line[1] == :on_ident and line[2] == "muffinize")
puts "muffinize found at line #{line.first.first}"
end
end
Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.
Here's a simple example:
class A
def foo
bar
muffinize(1)
end
end
A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.
By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.

Resources