DEV Community

loading...

Bypassing the database and testing to see if this solution works

chenge profile image chenge Updated on ・2 min read

I was thinking about how to not rely on the database in the test a few years ago, but I have not found a viable solution. Seeing this article today, I feel that the proposal proposed by the author seems to be feasible and relatively simple.
How to test that you are creating a record without using the database

I simply verified it, no problem.

I plan to do a real project verification later.

class Product < ActiveRecord::Base
end

store = Product

Catalog.add_product(attrs, store)

This is an example. The advantage of this is that it is more readable, you can simulate the store to avoid database testing.

Hey this is the test code


###
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

In the code, store is used instead of Product, and the store is replaced when testing.

I used the rspec test to be feasible, but the problem is that the verification code is not executed after the simulation, I don't know how to solve it.

Clean Architecture

As shown in the figure, the business model should be centered, the use case organizes the code, and the controller and database should be peripheral interfaces.
I feel that this code is to make the model as the entity, the corresponding Product class and class method as the database interface. Closer to the architectural idea of ​​the illustration. The original rails are organized with data as the center, or the database is the center.

Clean Architecture Description

Look at the code, how do you feel? Please see the original code for the detailed code.

Discussion (5)

pic
Editor guide
Collapse
bhserna profile image
Benito Serna

Hi @chenge , thanks for sharing my article =) ... although apparently the link is broken dev.to/bhserna/how-to-test-that-yo...

About the verification code...

I used the rspec test to be feasible, but the problem is that the verification code is not executed after the simulation, I don't know how to solve it.

I think that what Leonardo said is a good solution... I prefer the other just because you don't need to write allow(store).to receive(:create) before.

Other option is to create a custom "spy" that can do whatever you want, like...

class StoreSpy
  attr_reader :create_messages

  def initialize
    @create_messages = []
  end

  def create(attrs)
    create_messages << attrs
  end
end

store = StoreSpy.new

Catalog.add_product(attrs, store)
expect(store.create_messages).to include { name: "P1", description: "Super Product" }

#or

Catalog.add_product(attrs, store)
expect(store.create_messages).to eq [{ name: "P1", description: "Super Product" }]

But I think that in this case I prefer the other two solutions.

Abut the clean architecture reference...

I feel that this code is to make the model as the entity, the corresponding Product class and class method as the database interface. Closer to the architectural idea of ​​the illustration. The original rails are organized with data as the center, or the database is the center.

Yes! this is more or less what I do and propose... To use the model more or less like an entity and the Product class as the Database interface.

Collapse
leonardocouy profile image
Leonardo Couy

Nice post!!! That's unit test!! =DD

I'll give another way to test, but, using the rspec matcher have_received because it seems more readable. See the example below using have_received matcher and tell me what do you think, ok? =)

  it "creates a record" do
    ##### Setup
    attrs = { name: "P1", description: "Super Product" }
    allow(store).to receive(:create)

    ##### Execution
    Catalog.add_product(attrs, store)

    ##### Expectation
    expect(store).to have_received(:create).with(attrs)
  end

see more about have_received matcher

Collapse
chenge profile image
chenge Author

Thanks for reply. Here my main focus is how to run validation code while testing.

I'm a new comer for rspec so I cannot say too much about it.

Collapse
szymach profile image
Piotr Szymaszek

We have recently began relying on collection based repositories for functional tests (they store data/objects in memory, instead of saving it in the database). Unit tests should receive mock database connections and return mock data for database calls.

The idea is to split the testing environment into non persistent and persistent. This way you only test the database related bits when you know the code is working properly.

Collapse
chenge profile image
chenge Author

Yes, agree.