How to test that you are creating a record without using the database

April 26, 2018 0 Comments

How to test that you are creating a record without using the database

 

 


Do you want to know how you can use a test double or mock to test the creation of a record? Are you looking for examples of using mocks?... Maybe this post can help you =)

For better reference I have divided the task in 6 little steps:

Note: All the examples are using ruby and the rspec library, but if you want to understand the concept, I think you will be able to translate the examples to an other language without much trouble

1. Using an object to represent the database

As we are not going to use a "real database" we need something to use as the database... So we are going to expect to have an object that will represent the database... I like to use the word "store" to represent it because I try to express that is my "storage mechanism" but you can use the name that you want...

So if you are writing a Catalog application I would start with something like...

class DummyProductsStore 
end

2. How do you want to create the record?

Now that you have an object, you need think in the function or method do you want to be called on that object to perform the record creation...

For example, if you are writing a Catalog application and you want to save Products... you can expect to do something like

store = DummyProductsStore attrs = { name: "My Product", description: "The best product!" 
} store.create(attrs) #or
store.insert(attrs) #or
store.save(attrs)

or maybe you expect a behavior like...

store = DummyProductsStore product = Product.new( name: "My Product", description: "The best product!" 
) store.create(product) #or
store.insert(product) #or
store.save(product)

Personally I prefer to use the first way....

attrs = { name: "My Product", description: "The best product!" } 
store.create(attrs)

... I think that is easier if you expect to use something like the ActiveRecord library to talk with the database.

So... now that we have decided how we want our "store" to behave, let's write the method in our DummyProductsStore... but without nothing in it...

class DummyProductsStore def self.create(attrs) end 
end


3. What is going to trigger the creation of the record?

Normally when you want to use a test double to test the creation of a record, is because you have a higher level function that will trigger the expected behavior...

For example in our catalog application we want "create products" or "add products" so.. maybe we are exposing something like...

Catalog.addproduct(attrs, store) #or Catalog::AddProduct.call(attrs, store) 

Again... I prefer the first one but you can have your own taste...


4. Testing that the store was called right

Now that you have defined who is going to trigger the creation of a record, now you know where do you need to test it.

So inside the test file for that method (or object...) you can have one test like...

class DummyProductsStore def self.create(attrs) end 
end def store DummyProductsStore
end it "creates a record" do attrs = { name: "P1", description: "Super Product" } # This is the code that expects "store.create(attrs)" to be called expect(store).to receive(:create).with(attrs) Catalog.add
product(attrs, store)
end

And you can also write another test to for the cases that you don't want the store to be called...

describe "without name" do it "creates a record" do attrs = { name: "", description: "Super Product" } # This is the code that expects "store.create" not to be called expect(store).notto receive(:create) Catalog.addproduct(attrs, store) end 
end


5. Testing variations

In some cases you will compute something with the attributes that you are receiving... in that case you can add more specific cases with something like...

describe "creates a record adding a slug" do example do attrs = { name: "P1", description: "Super Product" } expect(store).to receive(:create).with(attrs.merge(slug: "p1")) Catalog.addproduct(attrs, store) end example do attrs = { name: "a FunKi Name", description: "Super Product" } expect(store).to receive(:create).with(attrs.merge(slug: "a-funky-name")) Catalog.addproduct(attrs, store) end 
end


6. How you can actually use this function in your app

You have designed the expected behavior for the storage system... This means that you can use your function with a "real" storage system that behaves in the same way...

Note: Maybe for the "real" storage you will need or want to write some integration tests with the actual storage device.

For example, if you are using active record you can just use the active record class directly... because it behaves in the same way =)

class Product < ActiveRecord::Base 
end store = Product Catalog.add_product(attrs, store)

... And I think that's all for now... I hope it helps =)

Do you need some help with TDD?

I am working on a guide to help you actually use TDD... You can visit this link to know a little more http://bhserna.com/spec-by-spec.html =)

This post was originally posted on http://bhserna.com


Tag cloud