Hi all, I asked this on StackOverflow and got some good answers but I’m curious as to how thoughtbotters test service classes. Let’s say a User signs up. A user is created in the database, is added to the email list, and other stuff happens. How do you test this?
class UserRegistrar
def sign_up(user)
User.create(user) # or something to this effect
EmailMarketing.add_to_email_list(user)
SuperSecretClass.do_secret_stuff(user)
LoggingThing.new.log_stuff_about(user)
end
end
(Controller action)
def create
UserRegistrar.sign_up(params)
# stuff for the strong params, etc...
end
Would you
Test that certain methods are called? (EmailMarketing.should_receive(:add_to_email_list).with...) Then you can test the individual effects in the class that owns it.
Test the effect of the code itself (expect { UserRegistrar.sign_up(user) }.to change{ user.persisted? }.from(false).to(true)), which is more thorough but takes a bit longer.
Following on from @benorenstein’s point, possibly using some observers would split up the responsibilities of UserRegistrar. The sign_up method could simply publish a topic that says “Hey, whoever is listening, I’ve signed this guy up!”. You’d then have listeners that would do your “Secret thing” and another that would log the successful signup. You could try the new Wisper gem which looks like a good choice. Here’s a quick lightning talk about it
Thank you @benorenstein and @aaronmcadam, most probably, I’m just not structuring the app correctly. I’m not sure though how you can lessen the number of collaborators. Let’s say hypothetically, when a user signs up to myapp.com via Github, we
Create a user row on the database
Add user to our mailing list (MailChimp).
Ask Resque/Delayed Job to queue saving the user’s avatar
Save the user’s repos to the database
How would you split this class?
(I’ll check the observers, but I’m a bit tentative about them, because I end up with code in random places… if I’m just going to use a callback once I’ll usually just inline the method call.)
One of the tradeoffs of OO is indirection and not being able to see a full procedure in one place. What we get in return is more maintainable and changeable objects. This video does a great job of summing up that idea.
I also refer to the legend that is Sandi Metz on SOLID principles.