I'm quite new to Rspec testing in Rails and I'm trying to test the following:
class Event < ApplicationRecord
def host_details
query ||= User.where(id: self.user_id)
end
def hosting_company
company = host_details.first.company
end
end
I have the following spec but I'm not sure exactly what I'm supposed to be expecting back for the results of the host_details method. I'm using FactoryGirl.
describe "gets information about the host of an event" do
before(:each) do
#event = build(:event)
end
context "host_details" do
it "querys the event data and finds the associated user" do
end
end
context "hosting_company" do
it "gets the company that is hosting the event" do
expect(#event.hosting_company).to eq "Attnd"
end
end
end
Related
I'm trying to test a class with RSpec which makes use of Searchkick.
I have the following in my spec support directory:
RSpec.configure do |config|
config.before(:suite) do
# Reindex models
Restaurant.reindex
# and disable callbacks
Searchkick.disable_callbacks
end
config.around(:each, search: true) do |example|
Searchkick.callbacks(true) do
example.run
end
end
end
and my test looks like this:
describe Restaurants::SearchFacade, search: true do
subject { described_class }
describe '#search' do
context 'with a query' do
let!(:restaurant_1) { create(:restaurant, :reindex, description: 'Amazing sour dough pizza place') }
let!(:restaurant_2) { create(:restaurant, :reindex) }
it 'returns values containing the query' do
Restaurant.search_index.refresh
params = { search: { query: 'sour dough' } }
facade = subject.new(params)
expect(facade.search.total_count).to eq(1)
end
end
end
end
I should be receiving 1 result. However, Searchkick returns 0 results. This is not a problem with the facade since I've also tried with Restaurant.search('*') which should return all results but nothing is being returned.
My Restaurant class looks like this:
class Restaurant < ApplicationRecord
searchkick searchable: %i[title description street_address]
end
and my factory looks something like this:
FactoryBot.define do
factory :restaurant do
association :plan
association :company
sequence :title do |n|
"Restaurant #{n}"
end
description 'An awesome restaurant'
# More attributes here...
trait :reindex do
after(:create) do |restaurant, _evaluator|
restaurant.reindex(refresh: true)
end
end
end
end
Any idea what may be causing this? Any help would be much appreciated. Thanks in advance!
Try getting rid of all of that fancy stuff you are doing in your before(:suite) and your factories. You just need to be doing it before each spec (if the search tag is set).
I was able to get searchkick working with rspec and indexing my models with only this small amount of code in my spec_helper.rb:
RSpec.configure do |config|
config.before(:each) do |example|
# Elasticsearch / Searchkick
if example.metadata[:search]
Searchkick.enable_callbacks
Restaurant.reindex
else
Searchkick.disable_callbacks
end
end
end
Side note: You may still need to run the Restaurant.search_index.refresh code after creating a bunch of Restaurant records in your spec setup. I'm not entirely sure?
I am writing a small program for a train system.
I have a passenger, coach, train and station class (and thus, a spec test for each).
My test for my passenger class is as such:
let (:passenger) {Passenger.new}
it "should not be touched in to a station when initialized" do
expect(passenger.touchedin?).to be false
end
it "should be able to enter coach" do
coach = Coach.new
passenger.enter(coach)
expect{coach.to receive(:enter)}
end
it "should be able to alight coach" do
coach = Coach.new
passenger.alight(coach)
expect{coach.to receive(:alight)}
end
it "should be able to touch into station" do
station = Station.new
passenger.touchin(station)
expect{station.to receive(:touchin)}
end
it "should be able to touch out of station" do
station = Station.new
passenger.touchout(station)
expect{station.to receive(:touchout)}
end
end
And my passenger class is like this (at the moment :p):
class Passenger
def initialize
#touchedin = false
end
def enter(coach)
end
def touchedin?
#touchedin
end
def alight(coach)
end
def touchin(station)
end
def touchout(station)
end
end
I am unsure how to satisfy my tests, if my tests are even correct in the first place.
Any help is really appreciated!
You've not really said how you're modeling the relationship between coaches and passengers, but one way I could think of could be as follows. I'm just putting enough for the coach/passenger relationship (so nothing about touching in as this involves the station) - and I'm using minitest syntax, but I think you can get the idea of what's happening.
class Coach
def initialize
#passengers = []
end
...
end
class Passenger
def initialize
#touched_in = false
end
def alight(coach)
coach.passengers << self.uid # or self, if you want the whole passenger object available
end
...
end
coach = Coach.new
assert_empty coach.passengers
joe = Passenger.new
refute_includes coach.passengers, joe.uid # or joe
joe.alight(coach)
assert_includes coach.passengers, joe.uid # or joe
My weakest point when it comes to coding, is using TDD & BDD methods - I tend to just write code.. but it is something that I am trying to work on.
Could anyone point out the best way to go about the following problem:
Class1:
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
if react_upon? xml.something
puts 'yeah'
else
puts 'nah'
end
end
def react_upon?(xml_code)
#code here
end
end
end
So lets say I wanted to test this class, or build it from a TDD point of view so I write my tests:
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it 'should check that it is valid' do
myclass.process '<some><xml>here</xml></some>'
end
it 'should output yea'
end
end
How do I test that it is calling the react_upon? method. Do I even want to see it is calling it?
Is the proper way to test it, to test all the functions like the react_upon? itself independently of the other functions?
This is properly the main thing that is most confusing me with this sort of testing. Am I testing the whole class, or just individually testing the functions, and not their interactions with the other functions in that class?
Also I realize the the react_upon? might not adhere to the Single responsibility principle and I would probably move that out to its own module/class which I could test using a stub.
If anyone can shed some light on this for me that would be awesome.
edit:
describe TempMod::MyClass do
let (:valid_planning_status_xml) {
'<StatusUpdate> <TitleId>2329</TitleId> <FromStatus>Proposed</FromStatus> <ToStatus>Confirmed</ToStatus> </StatusUpdate>'
}
let(:config) { double }
let(:status_resolver) { double }
subject(:message_processor) { TempMod::MyClass.new config, status_resolver }
context 'Given that the message XML is valid' do
it 'should check the context of the message' do
expect(message_processor.process valid_planning_status_xml).to call :check_me
end
context 'Given that the message is for a planning event update' do
it 'should call something' do
pending
end
end
context 'Given that the message is for a recording job update' do
end
context 'Given that the message is for a video title update' do
end
end
end
Your question confused me a bit is this what you are asking
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
react_upon?(xml.something) ? 'yeah' : 'nah'
end
def react_upon?(xml_code)
#code here
end
end
end
Then test like
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it "should respond to react_upon?" do
expect(myclass).to respond_to(:react_upon?)
end
it "should react_upon? valid xml" do
expect(myclass.react_upon?(YOUR VALID REACTION GOES HERE)).to be_true
end
it "should not react_upon? invalid xml" do
expect(myclass.react_upon?(YOUR INVALID REACTION GOES HERE)).to be_false
end
it "should say 'yeah' if it is valid" do
expect(myclass.process('<some><xml>here</xml></some>')).to eq('yeah')
end
it "should say 'nah' if it is invalid" do
expect(myclass.process('<some><xml>here</some>')).to eq('nah')
end
it 'should check the context of the message' do
expect(myclass).to receive(:react_upon?).with('<some><xml>here</xml></some>')
myclass.process('<some><xml>here</xml></some>')
end
end
end
Right now your tests have no expectations so I added one that expects myclass to respiond_to the react_upon? method and another that expects myclass.process(xml) to respond with a String that equals yeah.
I am running rspec tests on a catalog object from within a Ruby app, using Rspec::Core::Runner::run:
File.open('/tmp/catalog', 'w') do |out|
YAML.dump(catalog, out)
end
...
unless RSpec::Core::Runner::run(spec_dirs, $stderr, out) == 0
raise Puppet::Error, "Unit tests failed:\n#{out.string}"
end
(The full code can be found at https://github.com/camptocamp/puppet-spec/blob/master/lib/puppet/indirector/catalog/rest_spec.rb)
In order to pass the object I want to test, I dump it as YAML to a file (currently /tmp/catalog) and load it as subject in my tests:
describe 'notrun' do
subject { YAML.load_file('/tmp/catalog') }
it { should contain_package('ppet') }
end
Is there a way I could pass the catalog object as subject to my tests without dumping it to a file?
I am not very clear as to what exactly you are trying to achieve but from my understanding I feel that using a before(:each) hook might be of use to you. You can define variables in this block that are available to all the stories in that scope.
Here is an example:
require "rspec/expectations"
class Thing
def widgets
#widgets ||= []
end
end
describe Thing do
before(:each) do
#thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
# #thing is available here
#thing.should have(0).widgets
end
it "can get accept new widgets" do
#thing.widgets << Object.new
end
it "does not share state across examples" do
#thing.should have(0).widgets
end
end
end
You can find more details at:
https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#define-before(:each)-block
How would you test this class? Would you integrate it with Resque and check that the job gets put in the queue or would you mock it, and if you would mock it how would you avoid just duplicating the code like shown in example spec.
class BookingReminderEnqueuer
def initialize(user, booking, worker = BookingReminder)
#user, #booking, #worker = user, booking, worker
end
def enqueue
Resque.enqueue_at(
remind_user_at,
#worker,
booking_id: #booking.id,
user_id: #user.id
) if #booking.remind_user?
end
private
def remind_user_at
#booking.start_time - 6.hours
end
end
require 'spec_helper'
describe BookingReminderEnqueuer
describe "#enqueue" do
context "when the user should have a reminder" do
it "enqueues the reminder" do
booking.stub(:remind_user?) { true }
Resque.should_receive(:enqueue_at).with(
booking.start_time - 6.hours,
BookingReminder,
booking_id: booking.id,
user_id: user.id
).once
booking_reminder_enqueuer.enqueue
end
end
context "when the user shouldn't have a reminder" do
it "does not enqueue the reminder" do
booking.stub(:remind_user?) { false }
Resque.should_not_receive(:enqueue_at)
end
end
end
end
I would use a gem like fakeredis to create a test redis db. This will create an isolated in-memory Redis instance for testing that can be cleared around test runs. Then you can verify that the job is enqueued properly with the right parameters vs. mocking the enqueue method (since getting the enqueue parameters to work properly is the heart of this method.
# Gemfile
group :test do
gem "rspec"
gem "fakeredis", :require => "fakeredis/rspec"
end
# Spec
describe BookingReminderEnqueuer
describe "#enqueue" do
context "when the user should have a reminder" do
it "enqueues the reminder" do
booking.stub(:remind_user?) { true }
booking_reminder_enqueuer.enqueue
resque_job = Resque.peek(:queue_name)
resque_job.should be_present
# add assertions about job content/scheduling here
end
end
...
end
end