How to refactor common Geb test sequences

≡放荡痞女 提交于 2019-12-01 22:36:16

I think the 'geb' way to do this is to use modules.

You can create a login module like this:

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()
    }
}

Include it in your LoginPage:

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

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

    static content = {
        loginModule { module LoginModule }
    }
}

Then in your test, you can reference your module's login method:

@Stepwise
class EditPictureSpec extends GebSpec {

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

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

One possibility is to have one Spec for verifying the actual login behavior (e.g. LoginSpec) that is completely written out as it is now. For other Specs that need to login before doing the actual test you can abstract the entire login process behind a method in the LoginPage. Like you do now with singIn.

When you have a lot of Specs that need to login before they can really start testing the functionality they intend to test, doing the login steps through the browser again and again can take a lot of time.

An alternative can be to create a specific controller that is only loaded in the dev/test environments and that offers a login action. So instead of going through all steps (go to page, enter name, enter password, ...) you can simply go to the URL /my-app/testLogin/auth?username=username.

Below an example how we do this in our Grails + Spring Security setup. We also bundle other utility methods in that controller that are used in the setup of multiple Specs and that would otherwise require several clicks in the browser, e.g. changing the interface language.

// 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)
    }
}

You can create a login method and put it in a BaseSpec (that you would also create), which you would then extend in your tests. Eg:

class BaseSpec extends GebReportingSpec {

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

}

Since you're using @StepWise, I'm assuming you're logging in once per spec, so use setupSpec() thusly...

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

The correct solution is to create methods on a geb Page which encapsulate common functionality:

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
    }
}

Then your test looks like this:

@Stepwise
class ThingSpec extends GebSpec {

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

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

From an OOP perspective this is the best solution as the login procedure is only applicable to the login page. It doesn't make sense to use a module because no other page has a login box (unless it does, then modules make sense.)

It also doesn't make sense to use inheritance, you'll end up with an unorganized pile of methods and class names like "BaseSpec", bleh.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!