ivanibash
ivanibash

Reputation: 1491

Using controller create action in Factory Bot

I have a large and complicated #create method in my controller. While creating a model instance I am also saving a large file to a server, doing a bunch of processing on it, and some other stuff.

Now in my other tests I want all those 'side effects' to be available. So in Factory Bot it's not sufficient to simply create a model instance, so I need to run the whole #create action from the controller. My questions are:

What is the syntax for running the controller action in the factory file? Can't seem to find it.

Is that a normal practice for this sort of thing? Or should I avoid depending on controller's create action and write a minimal version of it in the factory?

Thanks.

Upvotes: 1

Views: 1471

Answers (3)

boyd
boyd

Reputation: 351

There's another way that could solve this problem, which is to take advantage of FactoryBot's callbacks. In my users#create action, I automatically add the users to a specific organization, so I've replicated this behavior for seed data by adding an after(:create) block to the users factory. I've also found that I can keep the rest of my test suite from getting slower by putting that after(:create) block inside a trait like so:

FactoryBot.define do
  factory :user do
    first_name { Faker::Name.unique.first_name }

    trait :with_organization do
      after(:create) do |user|
        org = Organization.find_by(name: 'Doctors Without Borders')
        FactoryBot.create(:organization_user, organization: org, user: user)
      end
    end
  end
end

Perhaps there's an even more elegant solution than this, but it's worked well for my needs thus far.

Upvotes: 0

ivanibash
ivanibash

Reputation: 1491

So after giving it some thought and with the help of @seancdavis I realised that FactoryBot is not a good fit for firing 'create' action. Instead I am running it in a before :each block as that seems to be the way to do it.

Upvotes: 0

seancdavis
seancdavis

Reputation: 2821

... in Factory Bot it's not sufficient to simply create a model instance

Factory Bot is not built to work with controllers, as it is focused solely on the object. As the repo description states, it is "A library for setting up Ruby objects as test data."

Now in my other tests I want all those 'side effects' to be available ... I need to run the whole #create action from the controller.

Think of Factory Bot as only there to mock model data for a test. Your factories don't care about what you have in your controller and views.

If you want to test controller actions, you should look at controller specs or request specs. (Links assume use of rspec.)

When writing a controller or request spec, you can use your factory to create an object that you can then use within your controller/request spec, therefore testing the effect your create action has on that object.

I have a large and complicated #create method in my controller.

I'd also suggest breaking your create action up if it's getting long. Two ways to accomplish this are through controller concerns or service objects. Both of these methods would also make testing easier because you can test each concern/service object separately.

You may also consider moving tasks into the background (or on the client side) if they become expensive because that can hold up the request and negatively affect performance.


To answer your questions directly:

What is the syntax for running the controller action in the factory file?

There isn't one because this is not what Factory Bot is built for.

Is that a normal practice for this sort of thing?

No.

Or should I avoid depending on controller's create action and write a minimal version of it in the factory?

Nope. But if the controller gets long and messy, feel free to break the logic up among concerns or service objects.

Upvotes: 2

Related Questions