Skip to content

Instantly share code, notes, and snippets.

@stevo
Created May 23, 2018 12:41
Show Gist options
  • Save stevo/2d7267e2d015116722ff8750ea7aa473 to your computer and use it in GitHub Desktop.
Save stevo/2d7267e2d015116722ff8750ea7aa473 to your computer and use it in GitHub Desktop.
The BDD story
# 1) All right... my task is to sync hotels with Cloudbeds. So probably this will be a rake task, but
# for simplicity I will just choose service as my outermost layer! Lets's dive in! I love BDD!
module Cloudbeds
describe SyncHotels do
# 2) it is a service, so to keep convention I'll test .call method
# Some small description of what I want it to do won't hurt as well.
# Scratch that! Description is essential!
describe '.call' do
it 'upserts hotels based on data returned from CloudBeds' do
# 7) Uh oh! I have realized that we will need to refresh a token once in a while.
# I need a place to store this bastard between expiries. Maybe "ApiCredential"?
# That is some nice name! Ok... so I'll store access_token here.
create(:api_credential, access_token: '123')
# 9) Dang! I thought that was it, but after reading description again, I've realized that
# I need to handle updating as well... Ok. Let's prepare some existing hotel.
existing_hotel = create(:hotel, cloudbeds_hotel_id: 100, name: 'Hilton')
# 4) Well... I need to get those hotels from Cloudbeds...
# How I would like to get them... hey! It is BDD! Anyhow I like! So maybe some wrapper?
# (I could use VCR here... but I would not see correlations between setup and data
# created... or maybe webmock... nah - to much knowledge about the structure of responses
# for this kind of spec). I will just ask wrapper for hotels and it will return me objects
# with everything I need! Sweeeeeet! I know it uses a token that needs to be refreshed
# every now and then, so I will go with approach of instantiating client instead of using
# static method like CloudbedsWrapper.get_properties. This way I'll be able to keep token
# in an instance. I'll assume this wrapper returns me one hotel - London Plaza - sounds PRO!
cloudbeds_wrapper = instance_double(CloudbedsWrapper)
allow(CloudbedsWrapper).to receive(:new) { cloudbeds_wrapper }
allow(cloudbeds_wrapper).to receive(:get_hotels) {
[
double(id: 99, name: 'London Plaza'),
# 10) Okie dokie. So I will return this extra hotel here for updating purposes.
# I need to remember that id should match the one I've created. And then we will
# change 'Hilton' to 'Paris'... because it is funny :D
double(id: 100, name: 'Paris'),
]
}
expect {
# 3) this is how I will call it. Simple!
SyncHotels.call
# 5) Cool, cool... so this should trigger creation of a hotel! Let's check the count...
}.to change { Hotel.count }.by(1)
# 6) Damn... count is not precise enough - let's check detailed attributes. I want to
# look for the hotel by its cloudbeds_hotel_id and then check if name matches the one
# I got from Cloudbeds. Neat!
new_hotel = Hotel.find_by(cloudbeds_hotel_id: 99)
expect(new_hotel).to have_attributes(name: 'London Plaza')
# 11) hahahaha... ok... calm down... now I need to verify, that this update works. Let's just
# reload that hotel and see if name is now updated!
expect(existing_hotel.reload).to have_attributes(name: 'Paris')
# 8) Ok - I have a place to store the token, but I need to make sure that my wrapper uses it!
# Let's do it! I'll assume that the token should be passed to constructor. Because why not?
expect(CloudbedsWrapper).to have_received(:new).with('123')
end
end
end
end
# 12) NICE! This spec looks N I C E! I can now dive in into implementation until it turns green.
# Probably I will need some more thorough specs for that CloudbedsWrapper because I've stubbed
# it (I'll just test what I use - CloudbedsWrapper#get_hotels), but for now I DO NOT HAVE TO CARE :D
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment