Groovy 定义Spock模拟行为

Groovy 定义Spock模拟行为,groovy,spock,Groovy,Spock,我正在写我的第一个Spock测试,并阅读了上面的文档,但在一些项目上仍然没有看到“穿越树木的森林” 我有一个类,MyRealm,它为我的应用程序执行身份验证。它有两个依赖项,AuthService和ShiroAdapter。前者我想嘲笑,后者我想保持现状(如果可能的话)。这是因为AuthService实际上建立了到LDAP的后端连接,所以我想模拟它。但是ShiroAdapter只定义了几个实用程序方法,将我的对象转换为apacheshiro安全对象(主体、权限等)。所以它可以不被模仿(我认为)

我正在写我的第一个Spock测试,并阅读了上面的文档,但在一些项目上仍然没有看到“穿越树木的森林”

我有一个类,
MyRealm
,它为我的应用程序执行身份验证。它有两个依赖项,
AuthService
ShiroAdapter
。前者我想嘲笑,后者我想保持现状(如果可能的话)。这是因为
AuthService
实际上建立了到LDAP的后端连接,所以我想模拟它。但是
ShiroAdapter
只定义了几个实用程序方法,将我的对象转换为apacheshiro安全对象(主体、权限等)。所以它可以不被模仿(我认为)

类MyRealmSpec扩展了规范{
我的王国
def设置(){
AuthService AuthService=Mock(AuthService)

//配置“authService”mock非常接近,检查抛出的异常类型的一种简单快捷的方法是将
异常
类放在括号中。例如:

def "authenticate throws ShiroException whenever auth fails"() {
    when:
    realm.authenticate('invalid_username', 'invalid_password')

    then:
    thrown(ShiroException)

}
您还需要模拟LDAP服务调用本身并模拟异常或失败的登录

def "authenticate throws ShiroException whenever auth fails"() {

    setup:
    String invalidUserName = 'invalid_username'
    String invalidPassword = 'invalid_password'

    when:
    realm.authenticate(invalidUserName, invalidPassword)

    then:
    1 * authService.doAuth(invalidUserName, invalidPassword) >> returnClosure  
    thrown(ShiroException)

    where:
    returnClosure << [{throw new ShiroException()}, { false }]
}

您可能会对几个不同的行为对象感兴趣

  • 存根-您只定义返回的内容

    MyObject obj = Stub{method >> null}
    
  • mock-定义返回的内容和/或调用方法的次数

    MyObject obj = Mock {1..3 methodCall >> false}
    
  • Spies-它创建您的对象,但您可以作为模拟重写特定方法(并且您的重写仍然可以调用原始代码)

听起来你需要一个存根,但是你可以使用一个没有任何问题的mock。我提到了spy,因为如果你的对象是自立的(将来),它是一个救生圈


注:在上述测试中,此测试

  • 模拟对返回值的计算(测试测试)
  • 调用authenticate和doAuth()之间发生的任何代码

  • 希望这就是你想要的。如果没有什么。身份验证()的逻辑可以打破(它的复杂性与一个吸气剂或SET方法),这个测试主要是浪费时间。逻辑的唯一方法可以打破,如果在JVM(完全超出了这个测试的责任)出了什么差错。,或者有人在将来某个时候做了更改(好吧,即使在.authenticate()包含不可破坏的基本逻辑的巨大假设下,测试也有一些价值);确保记住测试的内容和原因。这将帮助您确定测试用例的优先级,同时找出组织/分离测试逻辑的最佳方法。

    谢谢@Durandal(+1)-如何实现
    然后
    标签中的“或”逻辑?我将如何改变事情以实现“和”还有逻辑?再次感谢!在
    子句中是隐式的,每个模拟调用或列出的条件都会按提供的顺序进行检查;我只是在其中放置或,以显示返回异常与返回布尔值的示例。您可以添加
    where
    子句来在同一测试中同时执行这两个操作。我将更新以添加这一点你能把推荐信寄出去吗?
    MyObject obj = Stub{method >> null}
    
    MyObject obj = Mock {1..3 methodCall >> false}
    
    MyObject obj = Spy {methodCall >> false}  
    obj.otherMethodCall()  // Calls code like normal
    obj.methodCall() // Returns false like we told it to
    
    def "authenticate throws ShiroException whenever auth fails"() {
        given:
            AuthService authService = Stub(AuthService)
            authService.doAuth(_,_) >> expectedError
            MyRealm realm = new MyRealm(
                authService: authService, 
                shiroAdapter: new ShiroAdapter())
        when:
            realm.authenticate("just enough to get", "to the doAuth method")
        then:
            thrown(ShiroException)
        where:
            expectedError << [ShiroException, /*other exceptions this method has to test*/] 
    }
    
    def "authenticate throws ShiroException whenever auth fails"() {
        given:
            AuthService authService = Stub(AuthService)
            authService.doAuth(_,_) >> { args ->
               return args[0] == good && args[1] == good
            }
            MyRealm realm = new MyRealm(
                authService: authService, 
                shiroAdapter: new ShiroAdapter())
        expect:
            realm.authenticate(username, password) == expectedAuthentication
    
        where:
            userName | password | expectedAuthentication
             bad     |   good   |     false
             bad     |   bad    |     false
             good    |   good   |     true
    }