我有一个社交对象,负责连接到Twitter,Facebook等,并检索指定用户的提供商信息
对于每个提供者,我实现了一个单例TwitterAdapter,所有这些都继承自一个抽象类SocialAdapter
为了进行测试,我显然想要模拟TwitterAdapter,这样它就不会与twitter连接,而是返回一些固定的响应。
我发现的一个解决方案是使用隐式参数注入适配器列表。这个解决方案的问题是从其他函数调用Social.retrieveSocialProviderInfo,所以我必须通过所有调用链传递隐式List [SocialAdapter]参数,如下所示:
def createApplicationToken(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters)
: Either[List[Error], ApplicationToken] = {
// go to social info provider and fetch information
retrieveProviderInfo(accessToken).fold(
[...]
def retrieveProviderInfo(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters)
: Either[List[Error], IdentityProviderInfo] = {
[...]
最后
object Social {
val defaultAdapters = List(TwitterAdapter, FacebookAdapter)
def retrieveSocialProviderInfo
(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters) // adapters can be injected
: Option[IdentityProviderInfo] = {
[...]
你明白了
它工作正常,通常我只是忽略第二组参数并从Social.defaultAdapters中选择默认值,我在测试时只将其设置为List(MockTwitterAdapter,MockFacebookAdapter),但我只是为了能够使代码混乱测试它。
另一个解决方案是使Social.defaultAdapters成为var(而不是val),只需更改它以进行测试,通常在生产模式下,它总是具有相同的值。
我认为这一定是非常常见的情况。是否有更好的策略来处理这些情况?或者也许某种方式来扩展隐式赋值的范围?或者我应该使用功能齐全的依赖注入框架?
答案 0 :(得分:6)
一个简单的方法就是一直使用特征:
// you can test this trait and override the adapters as you wish
// by overriding the defaultAdapters member
trait Social {
implicit val defaultAdapters = List(TwitterAdapter, FacebookAdapter)
def retrieveSocialProviderInfo(accessToken: AccessToken):
Option[IdentityProviderInfo] = ...
}
// you can use this object directly in your production code
// if you don't want to mix it in
object Social extends Social
// or use the trait by mixing it with another
trait Application extends Social {
def createApplicationToken(accessToken: AccessToken):
Either[List[Error], ApplicationToken] = {
// the defaultAdapters are accessible to the
// retrieveProviderInfo method
retrieveProviderInfo(accessToken).fold(...)
}