Hi guys,
I was building this CRUD feature for an app and find myself trying to with an outside in approach for the code; and ended with not very Rails Way ‘way’ of doing things as my peers at work told me, saying that what do you think are really the problems of this particular code? and also of the tests. As you can see I’m trying to isolate myself from Rails entirely; also following Sandi Metz Magic Tricks on Testing rules.
module Admin
class DomainsController < Admin::ApplicationController
def index
@unknown_domains = DomainConfigs::ListUnknowns.list(DomainConfig)
@attempted_signups = AttemptedSignups::ListRecent.list(AttemptedSignup)
end
def new
@domain = DomainConfig.new
@attempted_signups = []
@imap_configurations = []
end
def edit
@attempted_signups = AttemptedSignups::LastFailed.list(
AttemptedSignup, domain_config.domain)
@imap_configurations = ImapConfigurations::Last.list(
ImapConfiguration, domain_config.domain)
end
def create
@domain = DomainConfigs::Creator.create(DomainConfig, params[:domain_config])
@attempted_signups = []
@imap_configurations = []
if @domain.valid?
flash[:notice] = 'Domain config created successfully'
redirect_to admin_domain_configs_path
else
flash[:error] = 'Domain config did not get created'
render action:'new'
end
end
end
module DomainConfigs
class Creator
def self.create(domain_config_repo, attributes)
new(domain_config_repo).create(attributes)
end
def initialize(domain_config_repo)
@domain_config_repo = domain_config_repo
end
def create(attributes)
domain_config_repo.save_domain_config(attributes.fetch(:status),
attributes.fetch(:host),
attributes.fetch(:domain),
attributes.fetch(:username_hint),
attributes.fetch(:username_format),
attributes.fetch(:account_type),
attributes.fetch(:probe_methods))
end
private
attr_reader :domain_config_repo
end
end
require 'minitest/autorun'
require_relative '../../../lib/domain_configs/creator'
module DomainConfigs
class CreatorTest < Minitest::Unit::TestCase
def test_create
domain_config = Minitest::Mock.new
attributes = {status:'status',
host:'host',
domain:'domain',
username_hint:'username_hint',
username_format:'username_format',
account_type:'account_type',
probe_methods:'probe_methods'}
domain_config.expect(:save_domain_config, true, ['status',
'host',
'domain',
'username_hint',
'username_format',
'account_type',
'probe_methods'])
assert_equal true, Creator.create(domain_config, attributes)
domain_config.verify
end
end
end
module DomainConfigs
class ListUnknowns
def self.list(domain_config)
domain_config.unknown_domain_configs
end
end
end
require_relative '../../../lib/domain_configs/list_unknowns'
module DomainConfigs
class ListUnknownsTest < Minitest::Unit::TestCase
class DomainConfig < Struct.new(:host, :status, :domain, :account_type)
end
def test_list
domain_config = Minitest::Mock.new
domain_config.expect :unknown_domain_configs, []
DomainConfigs::ListUnknowns.list(domain_config)
domain_config.verify
end
end
end
I just paste some of the services and code; because the other services follow the same approach. The models have wrapped methods which encapsulate what I’m calling from the outside. My peers told me that I have too much inderiction on this and that I have needless abstraction that I probably should just call create!```` and ````update_attributes!
from within the controller and test actual logic not just expecting the call to be made from my service layer.
This is actually confusing for me because Sandi says that if we have an external dependency which do something in another place then we should just expect that method to be call and not test for side effects from the caller if that’s.
Any suggestion will be appreciate it; thanks