Home > Back-end >  Rspec unit testing without database
Rspec unit testing without database

Time:06-15

Guys bear with me on this. I would highly appreciate any advise provided.

So lets say I have a controller with the below code:

class PersonController < ApplicationController
  serialize_with personSerializer
  string: title
  int: age


  def update
    authorize(person, can_update?)
    person.update(title:title,age:age)

    serialize person
  end
end

so in terms of Bussines Logic we have here:

  1. check for authorize
  2. update the object
  3. return the serializer of the object ( its just a json result)

Now I want to test this piece of code with RSpec, but there is no DB to save or receive an object( well there was one earlier but i want to remove it when running tests).

So far I have tried using nulldb gem to remove the db dependency. But the problem arrives when the created object cannot be retrieved.

if I want to test the authorize (1) . It's a function of another gem that tests if the user can even use this controller. I need to completely mock it? if so, how? which tool to use?

same for the update, if I do person.update, how should i check if it works if i dont have access to the active recored?

CodePudding user response:

Unless this is a system test, you probably shouldn't be be testing #authorize's functionality in your controller tests. Same goes for ActiveRecord#update. Unit tests should test the logic that exists in the unit of code that you are testing, not external dependencies. Therefore a perfectly acceptable solution would be to assert that those methods are called:

it "#update updates a person when can_update? is true" do
  # However you are creating person objects w/o a database.
  # Can also be any object.
  person = ""
  title = "Bob"
  age = 81
  can_update = true

  expect_any_instance_of(YourController).to receive(:authorize).with(person, true)
  expect(person).to receive(:update).with({ title: title, age: age })

  patch update, params: { title: title, age: age }

  expect(response).to have_http_status(:ok)
end

If you still want to test the business logic that lives in 3rd party libraries, you can define separate tests that isolate the desired functionality:

describe "authorize" do
  it "authorizes a person with permission" do
    person = Person.new(name: "Bob", age: 81)

    # Replace with what you expect to happen
    expect(authorize(person, true)).to be_truthy
  end
end

If you are committed to testing your external business logic in the controller, you'll need to manually mock out every method that the third party library calls. I would highly not recommend this approach as it is

  • brittle: if the library changes you may have to mock out more methods
  • verbose: you'll be writing a lot of code that does very little
  • non-specific: you'll be testing external business logic in test that should be focused on which code is rendered from a request

CodePudding user response:

eventually, I used nulldb, and just mock everything. also used FactroyBot. There is no easy way to do so, just hard work and mock all the stuff you need. mainly the DB calls.

For the authorize part i mocked the policy used:

allow_any_instance_of(MyPolicy).to receive(:model).and_return(true)
  • Related