How to run method in test - ruby-on-rails

I simply want to run a method in my test and see if it works.
I tried the following line of code in my testing class:
UserPostcodesImport.add_postcodes_from_csv
My user_postcodes_import_test.rb:
require "test_helper"
require "user_postcodes_import"
class UserPostcodesImportTest < ActiveSupport::TestCase
it "works" do
UserPostcodesImport.add_postcodes_from_csv
end
end
My user_postcodes_import:
class UserPostcodesImport
class << self
def add_postcodes_from_csv
puts "it works"
end
end
end
I expect the console to print "it works" but it prints the error:
NoMethodError: undefined method `add_postcodes_from_csv'

So testing doesn't really work like that. What you need to do in this case is take a look at the test calls and do something like this
test "the truth" do
assert true
end
so you might have
class UserPostcodesImportTest < ActiveSupport::TestCase
it "works" do
test_string = UserPostcodesImport.add_postcodes_from_csv
assert !test_string.blank?
end
end
If you're using rspec, it might look like this:
class UserPostcodesImportTest < ActiveSupport::TestCase
{subject = UserPostcodesImport}
it "works" do
expect (subject.add_postcodes_from_csv).to_not be_nil
end
end
something like that...check rspecs syntax here: https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers
The critical part of that is the assert, which is basically what triggers the test to run. You're asking "when I do THIS, does it return true?"
I'd start by looking here: https://guides.rubyonrails.org/testing.html in order to get a better sense of testing best practices.

Related

How to test Rails model associations

Trying to test a module. It works when executed in rails console, but not when written as a test. Suppose the following:
MyModel
a) has_many :my_other_model
MyOtherModel
a) belongs to :my_model
Module example:
module MyModule
def self.doit
mine = MyModel.first
mine.my_other_models.create!(attribute: 'Me')
end
end
Now test:
require 'test_helper'
class MyModuleTest < ActiveSupport::TestCase
test "should work" do
assert MyModule.doit
end
end
Returns:
NoMethodError: NoMethodError: undefined method `my_other_models' for nil:NilClass
Now try the same thing in the console:
rails c
MyModule.doit
Works just fine. But why not as a test?
Your test database is empty when you run this test, so calling MyModel.first is going to return nil, then you try to chain an unknown method to nil. What you'll probably want for your test suite is a fixture, which is just sample data. For now, you you can just create the first instance to get the test to work.
test "should work" do
MyModel.create #assuming the model is not validated
assert MyModule.doit
end
You could also refactor your module. Adding if mine will only try to create the other models if mine is not nil. That would get the test to pass, but negates the purpose of your test.
def self.doit
mine = MyModel.first
mine.my_other_models.create!(attribute: 'Me') if mine
end

Testing class which uses refinements with RSpec

Let's say I've got refinement
module RefinedString
refine String do
def remove_latin_letters
#code code code code
end
end
end
and I use it inside my class Speech:
class Speech
using RefinedString
def initialize(text)
#content = text.remove_latin_letters
end
end
I've written tests for refinement in RSpec and now I'm testing Speech class
describe Speech
let(:text) { "ąńńóyińg" }
it 'should call my refinement' do
expect(text).to receive(:remove_latin_letters)
Speech.new(text)
end
end
but I get RSpec::Mocks::MockExpectationError: "ąńńóyińg" does not implement: remove_latin_letter
I don't think mocking it is a good solution (but I may be wrong! Is mocking the solution here?)
so I tried
let(:text) { described_class::String.new("ąńńóyińg") }
but the result is the same.
I don't want to explicitly call using RefinedString inside my RSpec (it should figure it out on its own, right?)
How to make RSpec aware of my refined methods?
We always want to test behavior, rather than implementation. To my mind, refinements change the behavior of other classes by virtue of being included, rather than having their own behavior. To use a somewhat clumsy analogy, if we were to test the reproductive behavior of a virus, we would have to introduce it into a host cell. We are interested in what happens to the host when the virus takes over (so to speak).
One approach is to build test classes with and without the refinement, e.g.:
class TestClass
attr_reader :content
def initialize(text)
#content = text.remove_latin_letters
end
end
describe "when not using RefinedString" do
it "raises an exception" do
expect { TestClass.new("ąńńóyińg") }.to raise_error(NoMethodError)
end
end
class RefinedTestClass
using RefinedString
attr_reader :content
def initialize(text)
#content = text.remove_latin_letters
end
end
describe "when using RefinedString" do
it "removes latin letters" do
expect(RefinedTestClass.new("ąńńóyińg").content).to eq "ńńóń"
end
end

RSpec: stubbing Rails.application.config value doesn't work when reopening classes?

I have an option defined in application config. My class I want to test is defined in a gem (not written by me). I want to reopen the class:
Myclass.class_eval do
if Rails.application.config.myoption=='value1'
# some code
def self.method1
end
else
# another code
def self.method2
end
end
end
I want to test this code using RSpec 3:
# myclass_spec.rb
require "rails_helper"
RSpec.describe "My class" do
allow(Rails.application.config).to receive(:myoption).and_return('value1')
context 'in taxon' do
it 'something' do
expect(Myclass).to respond_to(:method1)
end
end
end
How to stub application config value before running the code which reopens a class.
Wow, this have been here for a long time, but in my case what I did was:
allow(Rails.configuration.your_config)
.to receive(:[])
.with(:your_key)
.and_return('your desired return')
Specs passing and config values stubed correctly. =)
Now, the other thing is about your implementation, I think it would be better if you defined both methods and inside from a run or something you decided wich one to execute. Something like this:
class YourClass
extend self
def run
Rails.application.config[:your_option] == 'value' ? first_method : second_method
end
def first_method
# some code
end
def second_method
# another code
end
end
Hope this helps.
Edit: Oh yeah, my bad, I based my answer on this one.

How to stub an active record relation to test a where clause with rspec?

I've got a class that look like this:
class Foo < ActiveRecrod::Base
has_many :bars
def nasty_bars_present?
bars.where(bar_type: "Nasty").any?
end
validate :validate_nasty_bars
def validate_nasty_bars
if nasty_bars_present?
errors.add(:base, :nasty_bars_present)
end
end
end
In testing the #nasty_bars_present? I'd method like to write an rspec test that stubs the bars association but allows the where to execute naturally. Something like:
describe "#nasty_bars_present?" do
context "with nasty bars" do
before { foo.stub(:bars).and_return([mock(Bar, bar_type: "Nasty")]) }
it "should return true" do
expect(foo.nasty_bars_present?).to be_true
end
end
end
The test above gives an error about there being no method where for an array. How can I wrap the mock so the where will execute appropriately?
Thanks!
For RSpec 2.14.1 (it should also work for RSpec 3.1), I would try this:
describe "#nasty_bars_present?" do
context "with nasty bars" do
before :each do
foo = Foo.new
bar = double("Bar")
allow(bar).to receive(:where).with({bar_type: "Nasty"}).and_return([double("Bar", bar_type: "Nasty")])
allow(foo).to receive(:bars).and_return(bar)
end
it "should return true" do
expect(foo.nasty_bars_present?).to be_true
end
end
end
This way, if you call bars.where(bar_type: "Nasty") without the specific conditions in the where statement, you won't get the bar double with bar_type: "Nasty". It could be reusable for future mocking of bars (at least for returning a single instance, for multiple instances, you would add another double).

How would you test observers with rSpec in a Ruby on Rails application?

Suppose you have an ActiveRecord::Observer in one of your Ruby on Rails applications - how do you test this observer with rSpec?
You are on the right track, but I have run into a number of frustrating unexpected message errors when using rSpec, observers, and mock objects. When I am spec testing my model, I don't want to have to handle observer behavior in my message expectations.
In your example, there isn't a really good way to spec "set_status" on the model without knowledge of what the observer is going to do to it.
Therefore, I like to use the "No Peeping Toms" plugin. Given your code above and using the No Peeping Toms plugin, I would spec the model like this:
describe Person do
it "should set status correctly" do
#p = Person.new(:status => "foo")
#p.set_status("bar")
#p.save
#p.status.should eql("bar")
end
end
You can spec your model code without having to worry that there is an observer out there that is going to come in and clobber your value. You'd spec that separately in the person_observer_spec like this:
describe PersonObserver do
it "should clobber the status field" do
#p = mock_model(Person, :status => "foo")
#obs = PersonObserver.instance
#p.should_receive(:set_status).with("aha!")
#obs.after_save
end
end
If you REALLY REALLY want to test the coupled Model and Observer class, you can do it like this:
describe Person do
it "should register a status change with the person observer turned on" do
Person.with_observers(:person_observer) do
lambda { #p = Person.new; #p.save }.should change(#p, :status).to("aha!)
end
end
end
99% of the time, I'd rather spec test with the observers turned off. It's just easier that way.
Disclaimer: I've never actually done this on a production site, but it looks like a reasonable way would be to use mock objects, should_receive and friends, and invoke methods on the observer directly
Given the following model and observer:
class Person < ActiveRecord::Base
def set_status( new_status )
# do whatever
end
end
class PersonObserver < ActiveRecord::Observer
def after_save(person)
person.set_status("aha!")
end
end
I would write a spec like this (I ran it, and it passes)
describe PersonObserver do
before :each do
#person = stub_model(Person)
#observer = PersonObserver.instance
end
it "should invoke after_save on the observed object" do
#person.should_receive(:set_status).with("aha!")
#observer.after_save(#person)
end
end
no_peeping_toms is now a gem and can be found here: https://github.com/patmaddox/no-peeping-toms
If you want to test that the observer observes the correct model and receives the notification as expected, here is an example using RR.
your_model.rb:
class YourModel < ActiveRecord::Base
...
end
your_model_observer.rb:
class YourModelObserver < ActiveRecord::Observer
def after_create
...
end
def custom_notification
...
end
end
your_model_observer_spec.rb:
before do
#observer = YourModelObserver.instance
#model = YourModel.new
end
it "acts on the after_create notification"
mock(#observer).after_create(#model)
#model.save!
end
it "acts on the custom notification"
mock(#observer).custom_notification(#model)
#model.send(:notify, :custom_notification)
end

Resources