首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >定义Spock模拟行为

定义Spock模拟行为
EN

Stack Overflow用户
提问于 2015-10-10 10:17:34
回答 2查看 2.2K关注 0票数 1

我正在编写我的第一个Spock测试,并在嘲弄互动上阅读文档,但仍然没有在一些项目上看到“穿过树的森林”。

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

代码语言:javascript
复制
class MyRealmSpec extends Specification {
    MyRealm realm

    def setup() {
        AuthService authService = Mock(AuthService)
        // configure 'authService' mock  <-- ?????

        ShiroAdapter shiroAdapter = new ShiroAdapter()

        realm = new MyRealm(authService: authService, 
            shiroAdapter: shiroAdapter)
    }

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

        then:
        Throwable throwable = thrown()
        ShiroException.isAssignableFrom(throwable)
    }
}

我相信我是非常接近,但正在努力配置模拟的行为方式,我希望它的测试。Spock文档(上面链接的)似乎只记录了如何验证一个模拟方法被调用的次数。我对此不感兴趣。

在这里,MyRealm#authenticate(String,String)调用了引擎盖下的AuthService#doAuth(String,String)。因此,我需要我的模拟AuthService实例简单地返回false (指示失败的auth),或者在发生意外事件时抛出ServiceFaulException

我有什么想法可以完成这件事吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-10-10 14:46:40

您非常接近,检查抛出的异常类型的一种简单、快捷的方法是将Exception类放在括号中。例如:

代码语言:javascript
复制
def "authenticate throws ShiroException whenever auth fails"() {
    when:
    realm.authenticate('invalid_username', 'invalid_password')

    then:
    thrown(ShiroException)

}

您还需要模拟LDAP服务调用本身,并模拟异常或失败的登录。模拟操作在测试的然后子句中进行。

代码语言:javascript
复制
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 }]
}

请注意,您需要在模拟语句上使用参数匹配,或者使用通配符匹配。

要匹配任何字符串,可以使用下划线语法:

代码语言:javascript
复制
1 * authService.doAuth(_, _) >> false
票数 1
EN

Stack Overflow用户

发布于 2015-10-12 00:18:50

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

  • 存根-您只定义了获取returnedMyObject obj = Stub {方法>> null}
  • Mocks定义返回的内容和/或方法calledMyObject obj =mok{1.3 methodCall >> false}的次数
  • MyObject obj = Spy {methodCall >> false} obj.otherMethodCall() //调用代码类似于普通的obj.methodCall() //返回false

听起来你需要一个存根,但你可以使用一个模拟,没有任何问题。我提到间谍,因为如果你的目标在将来是自食其力的话,它是一种拯救生命的工具。

代码语言:javascript
复制
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*/] 
}

不需要数据/逻辑分离,但它是使测试更加灵活和可维护的好方法。虽然在这种情况下并不需要这样做,因为您只有一个异常可以抛出。

实际上,我会将您失败的身份验证和异常身份验证测试分开。他们看到的是根本不同的行为,测试这两种情况的测试逻辑有点不同。为了维护性/灵活性,避免在每个测试中测试太多(或太少)符合您的利益。

代码语言:javascript
复制
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
}

注意上面的测试,这个测试..。

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

希望这就是你想要的。如果.authenticate()的逻辑中没有任何东西可以破坏(它的复杂性与getter或setter方法一样),那么这个测试主要是浪费时间。逻辑崩溃的唯一方法是,如果JVM中出了问题(这完全超出了这个测试的责任范围),或者将来某个时候有人做了一些更改(好,即使在.authenticate()包含不可打破的基本逻辑的巨大假设下,测试也有一定的价值)。我漫不经心的离题(非常抱歉);一定要记住测试的内容和原因。它将帮助您对测试用例进行优先排序,并同时制定组织/分离测试逻辑的最佳方法。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33052855

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档