class Product
attr_reader :name, :price, :id
def initialize(id:, name:, price:)
@id = id
@name = name.to_s
@price = price.to_money
end
end
FactoryBot.define do
factory :product, class: 'Product' do
skip_create
id { Random.random_number }
name { 'iPod' }
price { '$299.99' }
initialize_with { new(id: id, name: name, price: price) }
end
end
test '#price returns a Money object' do
product = build(:product)
assert_instance_of Money, product.price
end
Static JSON fixtures rot quickly and don’t allow easy variation
Use factories to generate realistic data objects.
Factory methods tend to become large and difficult to grok
test '#call makes a request' do
stub_api_request(foo: 'bar', sometimes: false, maybe: 'no')
end
test '#call makes a request with fizz param' # TODO
private
def stub_api_request(foo: 'bar', sometimes: false, maybe: 'no', otherwise: 'yes')
{
# ...
}
end
factory :api_product_payload, class: Hash do
skip_create
transient do
product factory: :product
end
id { product.id }
name { product.name }
price { product.price }
timestamp { Time.now.iso8601 }
trait :pending do
status { "pending"}
end
initialize_with { attributes.except(:product).to_json }
end
factory :api_product_response,
aliases: [:api_v1_product_response],
class: Hash do
skip_create
transient do
product factory: :product
end
# Core JSON:API top-level keys
initialize_with do
{
data: {
id: product.id.to_s,
type: 'product',
attributes: {
name: product.name,
price: product.price,
timestamp: Time.now.iso8601,
},
relationships: {},
},
meta: {},
}.to_json
end
end
test '.create sends a POST to the /products endpoint with the payload' do
product = build(:product)
request_body = build(:api_product_payload, product: product)
response_body = build(:api_product_response, product: product)
stub_request(:post, "/products")
.with(body: request_body)
.to_return_json(body: response_body, status: 201)
described_class.create(product)
end
FactoryBot.define do
factory(:PAN, aliases: [:card_number], class: 'String') do
skip_create
transient do
starting_number { '4' }
remaining_numbers { ('0'..'9').to_a.sample(15).join }
end
trait :visa # same as default
trait :mastercard do
starting_number { '5' }
end
initialize_with { "#{starting_number}#{remaining_numbers}" }
end
end
class CleoBank::Transaction
def message_type
raw_transaction['MessageType']
end
def authorization?
message_type[1] == "1"
end
end
class MessageType
def initialize(mti)
@mti = mti.to_s
end
def version
@mti[0]
end
def message_class
@mti[1]
end
def message_function
@mti[2]
end
def message_origin
@mti[3]
end
def authorization?
message_class == '0'
end
end
class CleoBank::Transaction
def message_type
MessageType.new(raw_transaction['MessageType'])
end
delegate :authorization?, to: :message_type
end
FactoryBot.define do
factory(:ISO_8583_message_type, aliases: [:mti], class: 'MessageType') do
skip_create
version { '0' }
message_class { '2' }
message_function { '2' }
message_origin { '0' }
trait :authorization do
message_class { '0' }
end
# ...
initialize_with do
new([
version,
message_class,
message_function,
message_origin].join
)
end
end
end
https://thoughtbot.github.io/factory_bot/