MinitTest Spec confusion over second parameter - ruby

I'm new to BDD and I'm trying to play with MiniTest Spec:
require 'minitest/spec'
require 'minitest/autorun'
class Car
attr_accessor :type
def initialize(type)
#type = 'petrol'
end
end
describe Array do
it "must be diesel" do
Car.new('diesel').type.must_equal 'diesel'
end
end
This is great - running this I get the following output:
Failure:
test_0001_must_be_diesel(ArraySpec):
Expected "diesel", not "petrol".
Which makes sense - "Expected diesel, not petrol" is exactly what I'm expecting. If I place a second parameter in my must_equal statement (which I assume is the message I want to return on failure) - I get an odd result:
require 'minitest/spec'
require 'minitest/autorun'
class Car
attr_accessor :type
def initialize(type)
#type = 'petrol'
end
end
describe Array do
it "must be diesel" do
Car.new('diesel').type.must_equal 'diesel', 'it must be a diesel'
end
end
Running this I get:
1) Failure:
test_0001_must_be_diesel(ArraySpec):
it must be a diesel.
Expected "petrol", not "diesel".
For some reason, now it says "Expected petrol not diesel". So it seems that adding in what I assume is a message parameter (as it is in the Test Unit version) is making the assertion flip around.
Is the idea of a message param in the speccing framework void?

There's been some inconsistency in how MiniTest::Spec handles multiple arguments. It appears to have been fixed in https://github.com/seattlerb/minitest/commit/cd4fe89b0057edc2258876ad8c5f5e7e722f73c2.
Just install the latest version of MiniTest from RubyGems and you should be fine:
gem install minitest
Then add this on the top of your file to use the gem:
gem 'minitest'

Related

Rspec doesn't clear factory objects after some specs

Im using clear ruby (ruby 2.3.5) without Rails and ActiveRecord and trying to write specs for some service. And if I run only part of file (including problem spec), it passed. If I run the whole file - the problem spec is failed.
By the way, I already use rspec/retry, and it settings for 20 times retry (And I see in logs, that the problem spec is failed in all 20 times).
ballot_spec.rb
# frozen_string_literal: true
require 'spec_helper'
require 'byebug'
RSpec.describe Ballot do
describe '#run' do
let(:kingdoms) { build_list(:kingdom, 6) }
let(:service) { described_class.new(kingdoms) }
before { allow(service).to receive(:hold_ballot) }
it 'holds ballot if cannot_finish_ballot? returns true' do
allow(service).to receive(:cannot_finish_ballot?).and_return(true)
service.run
expect(service).to have_received(:hold_ballot).at_least(:once)
end
it "doesn't hold ballot if cannot_finish_ballot? returns false" do
allow(service).to receive(:cannot_finish_ballot?).and_return(false)
service.run
expect(service).not_to have_received(:hold_ballot)
end
it 'returns Struct object' do
expect(service.run.class.superclass).to be Struct
end
end
describe '#hold_ballot' do
let(:all_kingdoms) { build_list(:kingdom, rand(2..10)) }
let(:pretendents) { all_kingdoms.first(rand(2..all_kingdoms.count)) }
let(:message) { build(:message, from: all_kingdoms.sample, to: all_kingdoms.sample) }
let(:expected_message_count) { pretendents.count * all_kingdoms.count }
let(:new_service) { described_class.new(pretendents) }
before do
allow(Message).to receive(:new).and_return(message)
allow(message).to receive(:send)
new_service.send('hold_ballot')
end
\/\/ THE PROBLEM IS IN THIS SPEC \/\/
it 'prepares messages to all existed kingdoms from every pretendent' do
expect(Message).to have_received(:new).exactly(expected_message_count).times
end
it 'only 6 of messages will be selected to be sent' do
expect(message).to have_received(:send).exactly(6).times
end
it 'resets Kingdoms' do
allow(Kingdom).to receive(:reset)
new_service.run
expect(Kingdom).to have_received(:reset).at_least(:once)
end
end
end
when I run rspec spec/services/ballot_spec.rb:26 (for test whole method '#hold_ballot') every spec is passed.
when I run rspec spec/services/ballot_spec.rb, I got failure:
1) Ballot#hold_ballot prepares messages to all existed kingdoms from every pretendent
Failure/Error: expect(Message).to have_received(:new).exactly(expected_message_count).times
(Message (class)).new(*(any args))
expected: 4 times with any arguments
received: 260 times with any arguments
when I use byebug, I see, that class Kingdom has dozens of objects, but variables "all_kingdoms" and "pretendents" contains not more than 10 objects.
So the once rootcause I can see here - rspec doesn't clear kingdoms, created during testing method '#run', but how to push it destroy them? I don't use ActiveRecord, don't use Rails, so cannot tun "reload!" or "destroy" explicitly. Tried to run GC.start, but it doesn't help. What can I do? Many thanks! )
spec_helper.rb
# frozen_string_literal: true
require './models/kingdom.rb'
require './models/message.rb'
require './services/message_compose.rb'
require 'factory_bot'
require 'ffaker'
require 'capybara'
require 'rspec/retry'
require './spec/factories/kingdom.rb'
require './spec/factories/message.rb'
RSpec.configure do |config|
config.verbose_retry = true
config.display_try_failure_messages = true
config.around :each do |ex|
ex.run_with_retry retry: 20 unless ex.run_with_retry
end
config.include FactoryBot::Syntax::Methods
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
end

Use "vanilla" assert in Rspec?

How can someone use vanilla assert in Rspec?
require 'rspec'
describe MyTest do
it 'tests that number 1 equals 1' do
assert 1 == 1
end
end
The error I get:
undefined method `assert' for
#<RSpec::ExampleGroups::Metadata::LoadFile:0x00000002b232a0>
Notice that I don't want to use assert_equal, eq, should, or other mumbo jumbo.
You can do this pretty easily:
require 'rspec/core'
require 'test/unit'
describe 'MyTest' do
include Test::Unit::Assertions
it 'tests that number 1 equals 1' do
assert 1 == 2
end
end
(if you want to be able to run the tests by doing ruby foo.rb then you'll need to require rspec/autorun too). This pulls in all of those assertions. If you really don't want any extra assertions, just define your own assert method that raises an exception when the test should fail.
Conversely you can easily use rspec's expectation syntax outside of rspec by requiring rspec/expectations - rspec3 is designed to be modular.
Configure RSpec to use MiniTest
RSpec.configure do |rspec|
rspec.expect_with :stdlib
end
Then you can use all the asserts offered by MiniTest from the standard library.
...or Wrong
Alternatively, you can use Wrong if you like asserts with a block:
require 'wrong'
RSpec.configure do |rspec|
rspec.expect_with Wrong
end
describe Set do
specify "adding using the << operator" do
set = Set.new
set << 3 << 4
assert { set.include?(3) }
end
Inspired by this blog article on RSpec.info.

'Error: Cannot open "/home/<...>/billy-bones/=" for reading' while using pry and DataMapper

So, I'm trying to build a quick console program for my development needs, akin to rails console (I'm using Sinatra + DataMapper + pry).
I run it and launch cat = Category.new(name: 'TestCat', type: :referential). It gives me the following error:
Error: Cannot open "/home/art-solopov/Projects/by-language/Ruby/billy-bones/=" for reading.
What could be the cause of the problem?
console:
#!/usr/bin/env ruby
$LOAD_PATH << 'lib'
require 'pry'
require 'config'
binding.pry
lib/config.rb:
# Configuration files and app-wide requires go here
require 'sinatra'
require 'data_mapper'
require 'model/bill'
require 'model/category'
configure :production do
DataMapper::Logger.new('db-log', :debug)
DataMapper.setup(:default,
'postgres://billy-bones:billy#localhost/billy-bones')
DataMapper.finalize
end
configure :development do
DataMapper::Logger.new($stderr, :debug)
DataMapper.setup(:default,
'postgres://billy-bones:billy#localhost/billy-bones-dev')
DataMapper.finalize
DataMapper.auto_upgrade!
end
configure :test do
require 'dm_migrations'
DataMapper::Logger.new($stderr, :debug)
DataMapper.setup(:default,
'postgres://billy-bones:billy#localhost/billy-bones-test')
DataMapper.finalize
DataMapper.auto_migrate!
end
lib/model/category.rb:
require 'data_mapper'
class Category
include DataMapper::Resource
property :id, Serial
property :name, String
property :type, Enum[:referential, :predefined, :computable]
has n, :bills
# has n, :tariffs TODO uncomment when tariff ready
def create_bill(params)
# A bill factory for current category type
case type
when :referential
ReferentialBill.new params
when :predefined
PredefinedBill.new params
when :computable
ComputableBill.new params
end
end
end
If I substitute pry with irb in the console script, it goes fine.
Thank you very much!
P. S.
Okay, yesterday I tried this script again, and it worked perfectly. I didn't change anything. I'm not sure whether I should remove the question now or not.
P. P. S.
Or actually not... Today I've encountered it again. Still completely oblivious to what could cause it.
** SOLVED **
DAMN YOU PRY!
Okay, so here's the difference.
When I tested it the second time, I actually entered a = Category.new(name: 'TestCat', type: :referential) and it worked. Looks like pry just thinks cat is a Unix command, not a valid variable name.
Not answer to the pry question I just generally hate case statements in ruby.
Why not change:
def create_bill(params)
# A bill factory for current category type
case type
when :referential
ReferentialBill.new params
when :predefined
PredefinedBill.new params
when :computable
ComputableBill.new params
end
end
to:
def create_bill(params)
# A bill factory for current category type
self.send("new_#{type}_bill",params)
end
def new_referential_bill(params)
ReferentialBill.new params
end
def new_predefined_bill(params)
PredefinedBill.new params
end
def new_computable_bill(params)
ComputableBill.new params
end
You could make this more dynamic but I think that would take away from readability in this case but if you'd like in rails this should do the trick
def create_bill(params)
if [:referential, :predefined, :computable].include?(type)
"#{type}_bill".classify.constantize.new(params)
else
#Some Kind of Handling for non Defined Bill Types
end
end
Or this will work inside or outside rails
def create_bill(params)
if [:referential, :predefined, :computable].include?(type)
Object.const_get("#{type.to_s.capitalize}Bill").new(params)
else
#Some Kind of Handling for non Defined Bill Types
end
end

With Test::Unit, how can I run a bit of code before all tests (but not each test)?

In my test app, which uses test::unit, I need to start by pulling a bunch of data from various sources. I'd like to only do this once - the data is only read, not written, and doesn't change between tests, and the loading (and error checking for the loading), takes some time.
There are values that I DO want reset every time, and those are easy enough, but what if I want persistant accessible values? What's the best way to do this?
I'm especially interested in solutions that would let my push those assignments to some module that can be included in all my tests, since they all need access to this data.
Why do you need it inside the test? You could define it gloabl:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
GLOBAL_DATA = 11
class My_Tests < Test::Unit::TestCase
def test_1()
puts "Testing startup 1"
assert_equal(11, GLOBAL_DATA)
end
end
GLOBAL_DATA could be a (singleton)-class (respective an instance).
If you have only one testclass, you may use TestCase.startup:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
class My_Tests < Test::Unit::TestCase
def self.startup
puts "Define global_data "
##global_data = 11
end
def test_1()
puts "Testing 1"
assert_equal(11, ##global_data = 11)
end
def test_2()
puts "Testing 2"
assert_equal(11, ##global_data = 11)
end
end
You can just put them at the top of the class. They will get executed, and then your tests will get executed.
You could do this in the setup method:
def setup
if !defined?(##initial_data)
# Whatever you need to do to get your initial data
##initial_data = foo
end
#other_data = bar
end

Webrat Mechanize outside of Rails

I'm trying to use Webrat in a standalone script to automate some web browsing. How do I get the assert_contain method to work?
require 'rubygems'
require 'webrat'
include Webrat::Methods
include Webrat::Matchers
Webrat.configure do |config|
config.mode = :mechanize
end
visit 'http://gmail.com'
assert_contain 'Welcome to Gmail'
I get this error
/usr/lib/ruby/gems/1.8/gems/webrat-0.6.0/lib/webrat/core/matchers/have_content.rb:57:in 'assert_contain': undefined method assert' for #<Object:0xb7e01958> (NoMethodError)
assert_contain and other assertions are methods of test/unit, try to require it and use webrat from inside a test method:
require 'test/unit'
class TC_MyTest < Test::Unit::TestCase
def test_fail
assert(false, 'Assertion was false.')
end
end
anyway i haven't tested it but I have a working spec_helper for rspec if this can interest you:
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
require 'spec/rails'
require "webrat"
Webrat.configure do |config|
config.mode = :rails
end
module Spec::Rails::Example
class IntegrationExampleGroup < ActionController::IntegrationTest
def initialize(defined_description, options={}, &implementation)
defined_description.instance_eval do
def to_s
self
end
end
super(defined_description)
end
Spec::Example::ExampleGroupFactory.register(:integration, self)
end
end
plus a spec:
# remember to require the spec helper
describe "Your Context" do
it "should GET /url" do
visit "/url"
body.should =~ /some text/
end
end
give it a try I found it very useful (more than cucumber and the other vegetables around) when there is no need to Text specs (features) instead of Code specs, that I like the most.
ps you need the rspec gem and it installs the 'spec' command to execute your specs.

Resources