我有一个我正在实现 Twilio SMS API 的 Rails 应用程序,我对如何测试我的设计有点迷茫。
首先,我刚刚创建了一个模型,它是一个封装 twilio API 的 SMS 邮件程序,我希望能够对其进行测试并确保其功能,而不会用完 SMS 积分或用测试文本消息轰炸某人。
我知道如何实现 API 并让它在代码中工作,但我需要帮助实际上是测试代码以确保它工作并防止将来损坏。有人可以提供一些建议吗?
谢谢!
我有一个我正在实现 Twilio SMS API 的 Rails 应用程序,我对如何测试我的设计有点迷茫。
首先,我刚刚创建了一个模型,它是一个封装 twilio API 的 SMS 邮件程序,我希望能够对其进行测试并确保其功能,而不会用完 SMS 积分或用测试文本消息轰炸某人。
我知道如何实现 API 并让它在代码中工作,但我需要帮助实际上是测试代码以确保它工作并防止将来损坏。有人可以提供一些建议吗?
谢谢!
您可以使用我已经测试过的 gem Twilio.rb,然后在您的测试中模拟它,例如使用 mocha
Twilio::SMS.expects(:create).with :to => '+19175551234', :from => '+12125551234', :body => '这很简单!'
你的单元测试不应该影响外部服务,它们应该总是被嘲笑。这是从单元测试的一般原则得出的,即测试不应扩展被测试对象的类边界,并且应模拟/存根协作对象。
希望这可以帮助!
我的测试经验以及测试 Twilio 应用程序的经验是,您进行测试以消除添加的风险。您将希望使用Twilio gem,而不是针对他们的 REST 端点滚动您自己的 SMS 代码:这可以最大限度地降低风险。
尽可能将 API 封装在业务逻辑类中,并主要测试业务逻辑。例如,在我的系统中,SMS 是从 Reminder 类中发送出去的。代码看起来像这样:
class SomeWrapperClass
if (RAILS_ENV == "testing")
@@sent_smses = []
cattr_accessor :sent_smses
end
def send_a_message(to, from, message, callback_url = nil)
unless RAILS_ENV == "testing"
Twilio::SMS.message(to, from, message, callback_url)
else
@@sent_smses << {:to => to, :from => from, :message => message, :callback_url => callback_url}
end
end
end
这让我可以编写专注于我的业务逻辑的测试,这是我要搞砸的东西。例如,如果我想测试一些发送 SMS 消息的方法 send_reminder(client):
test "sends reminder to client" do
SomeWrapperClass.sent_smses = []
client = clients(:send_reminder_test_case)
Reminder.send_reminder(client)
sent_message = SomeWrapperClass.sent_smses.last
assert !sent_message.blank?, "Sending a reminder should fire an SMS to client."
assert sent_message.index(client.name) >= 0, "Sending a reminder should fire an SMS with the client's name in it.
...
end
现在我正在测试我添加的实际风险,即我搞砸了 Reminder.send_reminder。另一方面,包装器应该接近无风险。
显然,尽可能多地分离逻辑。通过这样做,您可以尽可能多地测试其他所有内容,然后只将调用留给需要测试的外部 API。
使用外部 API 可能会很棘手。一种选择是模拟对您知道对您有用的东西或您期望的响应的响应,但这显然有点脆弱。另一种选择是查看类似VCR的内容。这将记录一次对外部 API 的调用,并在您再次调用时再次播放。
这家伙似乎已经开始解决你的问题了:https ://github.com/arfrank/Fake-Twilio-Api
您可能不需要测试 twiliolib 的代码,但如果您不想存根 twiliolib 的方法,您可以使用 FakeWeb gem,在其中定义指定请求的响应。
与史蒂夫提到的类似,我只是用 mocha 将请求存根:
# In Twilio initializer
TWILIO_ACCOUNT = Twilio::RestAccount.new(TWILIO_CONFIG[:sid], TWILIO_CONFIG[:token])
# In a test helper file somewhere
class ActiveSupport::TestCase
# Call this whenever you need to test twilio requests
def stub_twilio_requests
# Stub the actual request to Twilio
TWILIO_ACCOUNT.stubs(:request).returns(Net::HTTPSuccess.new(nil, nil, nil).tap { |n|
n.stubs(:body).returns("<?xml version=\"1.0\"?>\n<TwilioResponse></TwilioResponse>\n")
})
end
end