这是一个很好的做法,通常有几个原因:
从隔离的角度来看:控制器的职责是处理传入的请求并相应地触发动作。在我们给定的案例中,操作是:创建一个新工作,如果一切正常,则向 Facebook 发布一个新帖子。(请注意我们的控制器不需要知道如何发布到 FB)
所以想象以下控制器动作:
def create
job = Job.new job_params
if job.save
FacebookService.post_job job
...
else
...
end
end
我会像这样测试它:
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
另一个优点是:FacebookService.post_job
可能需要大量时间,并且可能需要访问 Internet 等,我们不希望我们的测试在这些上挂起,特别是如果我们有 CI。
最后,我会在 FacebookService 测试中测试真正的 FB 发布,并且可能会删除一些其他方法,以防止在测试运行时每次都在 FB 上发布(它需要时间、互联网、FB 帐户......)。