使用Sourcery在iOS中自动生成委托存根

代表们,代表们! iOS喜欢代表。 它们在Apple的大多数框架中都使用过,我对您一无所知,但最终我在自己开发的每个应用程序中一遍又一遍地编写自己的应用程序。 在测试代​​码时,您必须编写许多手动存根和样板来测试调用委托的类,谁愿意编写比他们需要的代码更多的代码! 任何额外的工作都会使测试在整个团队中进行的可能性降低,并且在进行测试时会变得越来越精明。 每当您定义代理人BAM时,Sourcery都可以在此提供帮助。 在下一次编译时,会生成一个存根并将其添加到您的测试目标中,所有内容都会充实并准备就绪。

如果您不知道Sourcery是什么,它是一个元编程工具包,可以“ 扫描源代码,应用个人模板并为您生成Swift代码,从而使您可以使用元编程技术来节省时间并减少潜在的错误。” 这太疯狂了。 这篇文章不是Sourcery的完整介绍(尽管我们将在您的项目中进行设置)。 如果您想查看一个好的介绍,请转到Ray Wanderlich的教程。 它很好地解释了它并引导您完成它。

这是我们将做的一个例子:

代表定义:

 协议AutoStub {} 
协议OnboardingViewDelegate:AutoStub {
func tappedLogin(用户名:字符串,密码:字符串)->布尔
func tappedRegister(用户名:字符串,密码:字符串)
}

生成的存根:

  //使用Sourcery 0.7.2生成-https://github.com/krzysztofzablocki/Sourcery 
//不要编辑
类OnboardingViewDelegateStub:OnboardingViewDelegate {
var namedTappedLogin:Bool = false
var称为TappedLoginWithParamUsername:字符串?
var namedTappedLoginWithParamPassword:字符串?
var tappedLoginReturnValue:布尔?
func tappedLogin(用户名:字符串,密码:字符串)->布尔{
self.drawnTappedLogin = true
self.CalledTappedLoginWithParamUsername =用户名
self.CalledTappedLoginWithParamPassword =密码
返回self.tappedLoginReturnValue!
}

var namedTappedRegister:Bool = false
var namedTappedRegisterWithParamUsername:字符串?
var称为TappedRegisterWithParamPassword:字符串?
func tappedRegister(用户名:字符串,密码:字符串)->无效{
self.drawnTappedRegister = true
self.drawnTappedRegisterWithParamUsername =用户名
self.drawnTappedRegisterWithParamPassword =密码
}
}

入门

1.将Sourcery添加到您的项目

我假设您正在使用cocopoads,尽管如果您愿意,也可以将Sourcery直接安装到您的计算机上。 无论如何,首先将Sourcery添加到Podfile并运行pod install

  pod'Sourcery' 

2.在“构建阶段”中添加一个“运行脚本”,以在构建时生成模板。

运行脚本:

  $ {PODS_ROOT} / Sourcery / bin / sourcery --sources $ {PROJECT_DIR}-模板$ {PROJECT_DIR} / templates-输出$ {PROJECT_DIR} / 

如果您现在尝试构建项目,Sourcery将抱怨并且构建将失败。 那是因为没有“模板”文件夹可供它从中加载模板。 因此,继续在根项目目录中创建一个“ templates”文件夹,(如果需要,也可以将其添加到Xcode项目中)。

3.创建模板:

在您的模板目录中创建一个文件:’DelegateStub.stencil'{##}里面的任何内容都是注释,不输出。

  {#我们将遍历实现AutoStub#}的项目中的所有类型 
{types.implementing.AutoStub%中的类型所占的百分比}
{#然后创建一个符合它的类#}
类{{type.name}}存根:{{type.name}} {
{#^打印:类OnboardingViewDelegateStub:OnboardingViewDelegate {
然后,我们将为所有方法创建属性存根,这样您就可以检查存根中已调用了哪种方法以及使用了哪些参数。
{type中的方法的%.allMethods%}
var称为{{method.callName | upperFirst}}:布尔=假
{#^打印:var namedTappedLogin:Bool = false#}
{%为method.parameters%中的参数}
var被称为{{method.callName | upperFirst}} WithParam {{parameter.name | upperFirst}}:{{parameter.typeName}}?
{#^打印var称为TappedLoginWithParamUsername:字符串? #}
{%endfor%}
{如果method.returnTypeName.name!=“无效”%}
var {{method.callName}}返回值:{{method.returnTypeName}}?
{% 万一 %}
[#^如果返回类型不是Void,则存储一个可选属性,该属性将在存根中返回

生成存根并设置所有属性#}

func {{method.name}}-> {{method.actualReturnTypeName}} {
self。Called {{method.callName | upperFirst}} = true
{%为method.parameters%中的参数}
自我调用{{method.callName | upperFirst}} WithParam {{parameter.name | upperFirst}} = {{parameter.name}}
{%endfor%}


{如果method.returnTypeName.name!=“无效”%}
返回自己。{{method.callName}}返回值!
{% 万一 %}
{#^如果返回类型不是Void,则强制展开并返回存储的return属性#}
}
{%endfor%}
}
{%endfor%}

4.写一些代表

像往常一样编写您的代表,但要确保在项目中添加一个空白协议’AutoStub’,并使要生成桩的每个协议都符合该协议。

 协议AutoStub {} 
 协议OnboardingViewDelegate:AutoStub { 
  func tappedLogin(用户名:字符串,密码:字符串)->布尔 
  func tappedRegister(用户名:字符串,密码:字符串) 
}

尽管使用当前模板不能,但是您可以对其进行修改,甚至可以对Apple的委托或其他库中包含的委托进行存根处理。

5.建立您的项目并编写测试! 喔!

生成存根后,将创建一个名为“ DelegateStub.genic.swift”的文件。 您可以将其拖到Xcode中并将其添加到测试目标中,以备使用。

而且,仅此而已。 有任何问题或建议吗? 认为存根是愚蠢的还是喜欢使用存根? 让我在下面知道。