如何重构常见的Geb测试序列

时间:2014-05-08 04:15:53

标签: grails spock geb

假设我有多个登录的Geb / Spock测试。例如:

@Stepwise
Class AddNewPictureSpec extends GebSpec {
  def "User at login page"() {
    given: "User beings from login page"
    to LoginPage
  }
  def "User gets redirected to Main page"() {
    given: "User at Login page"
    at LoginPage

    when: "User signs in"
    signIn "username", "pw"
    to MainPage

    then:
    at MainPage

  def "other test sequences follow...."() {
  }    
}

另一个具有完全相同启动顺序的测试规范:

@Stepwise
Class EditPictureSpec extends GebSpec {
      def "User at login page"() {
        given: "User beings from login page"
        to LoginPage
      }
      def "User gets redirected to Main page"() {
        given: "User at Login page"
        at LoginPage

        when: "User signs in"
        signIn "username", "pw"
        to MainPage

        then:
        at MainPage    

  def "other test sequences follow...."() {
      }
    }

如何重构/提取常用登录“步骤”,以便我没有重复的代码?还是我错误地写了我的测试?感谢。

4 个答案:

答案 0 :(得分:6)

我认为'geb'方法是使用modules

您可以创建如下的登录模块:

class LoginModule extends Module {  
    static content = {
        loginForm {$("form")}
        loginButton {$("input", value: "Sign in")}
    }

    void login(String username, String password = "Passw0rd!") {
        loginForm.j_username = username
        loginForm.j_password = password
        loginButton.click()
    }
}

将其包含在LoginPage

class LoginPage extends Page {
    static url = "login/auth"   

    static at = {title == "My Grails Application"}

    static content = {
        loginModule { module LoginModule }
    }
}

然后在测试中,您可以引用模块的login方法:

@Stepwise
class EditPictureSpec extends GebSpec {

    def setupSpec() {
        to LoginPage
        loginModule.login(loginUsername)
    }

    def "some test"() {
        ...
    }
}

答案 1 :(得分:2)

一种可能性是使用一个Spec来验证完全按原样写出的实际登录行为(例如LoginSpec)。对于在进行实际测试之前需要登录的其他规范,您可以在LoginPage中的方法后面抽象整个登录过程。就像你现在使用singIn一样。

如果你有很多需要登录才能真正开始测试他们打算测试的功能,那么一次又一次地通过浏览器执行登录步骤会花费很多时间。

另一种方法是创建一个特定的控制器,该控制器仅在开发/测试环境中加载并提供登录操作。 因此,您可以直接转到网址/my-app/testLogin/auth?username=username,而不是完成所有步骤(转到页面,输入名称,输入密码......)。

下面是我们在Grails + Spring Security设置中如何做到这一点的示例。我们还在该控制器中捆绑了其他实用程序方法,这些方法用于设置多个规范,否则在浏览器中需要多次点击,例如改变界面语言。

// Example TestLoginController when using the Spring Security plugin
class TestLoginController {

def auth = { String userName, String startPage = 'dashboard' ->

    // Block the dev login functionality in production environments
    // Can also be done with filter, ...
    Environment.executeForCurrentEnvironment {
        production {
            render(status: HttpServletResponse.SC_NOT_FOUND)
            return
        }
    }

    def endUser = getYourEndUserDataByUsername()
    if (endUser) {
        // Logout existing user
        new SecurityContextLogoutHandler().logout(request, null, null)

        // Authenticate the user
        UserDetails userDetails = new User(endUser)
        def authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, userDetails.password, userDetails.authorities)
        SecurityContextHolder.context.setAuthentication(authenticationToken)

        // Bind the security context to the (new) session
        session.SPRING_SECURITY_CONTEXT = SecurityContextHolder.context

        redirect(action: "index", controller: startPage)
    }
}

答案 2 :(得分:2)

您可以创建一个登录方法并将其放入BaseSpec(您也将创建),然后在测试中扩展。例如:

class BaseSpec extends GebReportingSpec {

  def login(name, pw) {
    to LoginPage
    // login code here... 
  }

}

由于您正在使用@StepWise,我假设您每个规范都登录一次,因此请使用setupSpec()...

Class AddNewPictureSpec extends BaseSpec {
  def setupSpec() {
    login("username", "password")
  }
}

答案 3 :(得分:1)

正确的解决方案是在geb页面上创建封装常用功能的方法:

class LoginPage extends Page {
    static url = "login/auth"   

    static at = {title == "Login"}

    static content = {
        username { $("#user") }
        password { $("#password") }
    }

    def login(String email, String passwd) {
        emailInput.value(email)
        passwordInput.value(passwd)
        passwordInput << Keys.ENTER
    }
}

然后你的测试看起来像这样:

@Stepwise
class ThingSpec extends GebSpec {

    def setupSpec() {
        to LoginPage
        page.login("user", "pass")
    }

    def "some test"() {
        ...
    }
}

从OOP角度来看,这是最佳解决方案,因为登录过程仅适用于登录页面。使用模块是没有意义的,因为没有其他页面有登录框(除非它有,否则模块是有意义的。)

使用继承也没有意义,你最终会得到一堆无组织的方法和类名,比如“BaseSpec”,bleh。