It is a good practice generally for several reasons:
From an isolation point of view: The responsibility of the controller is to handle the incoming request and trigger actions accordingly. In our given case the actions are: create a new Job, and issue a new post to Facebook if everything fits. (Please notice our controller doesn't need to know about how to post to FB)
So imagine the following controller action:
def create
job = Job.new job_params
if job.save
FacebookService.post_job job
...
else
...
end
end
I would test it like:
class JobsControllerTest < ActionController::TestCase
test "should create a job and issue new FB post" do
job_params = { title: "Job title" }
# We expect the post_job method will be called on the FacebookService class or module, and we replace the original implementation with an 'empty/mock' method that does nothing
FacebookService.expects :post_job
post :create, job_params
assert_equal(Job.count, 1) # or similar
assert_response :created
end
end
The other advantage is: FacebookService.post_job
might take significant time, and might require internet access etc, we don't want our tests to pending on those, especially if we have CI.
And finally I would test the real FB posting in the FacebookService test, and maybe stub out some other method, to prevent posting on FB every single time when the test runs (it needs time, internet, FB account...).