How to Reorganize Generic Geb Test Sequences

Suppose I have several Geb / Spock tests that are logged-in creatures. For example:

@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...."() { } } 

And one more test specification with the same sequence:

 @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...."() { } } 

How do I refactor / extract common login steps so that I don't have duplicate code? Or am I writing my tests incorrectly? Thanks.

+2
grails spock geb
source share
4 answers

I think the way geb does this is to use modules .

You can create a login module as follows:

 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 login method:

 @Stepwise class EditPictureSpec extends GebSpec { def setupSpec() { to LoginPage loginModule.login(loginUsername) } def "some test"() { ... } } 
+5
source share

One of the possibilities is to have one Spec for checking the actual login behavior (for example, LoginSpec ), which is completely written out as it is now. For other specifications that need to be logged in before performing the actual test, you can abstract the whole process of logging in to the method in LoginPage . Like now, singIn .

When you have many specifications that need to be logged in before they can begin testing the features that they intend to test, re-entering the browser through the browser can take a lot of time.

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

Below is an example of how we do this in our Grails + Spring security configuration. We also combine other utilities in this controller that are used to configure several specifications, otherwise it would take a few clicks in the browser, for example. change 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) } } 
+1
source share

You can create an input method and put it in BaseSpec (which you will also create), which will then apply to your tests. For example:

 class BaseSpec extends GebReportingSpec { def login(name, pw) { to LoginPage // login code here... } } 

Since you are using @StepWise, I assume that you register once for each specification, so use setupSpec () this way ...

 Class AddNewPictureSpec extends BaseSpec { def setupSpec() { login("username", "password") } } 
+1
source share

The correct solution is to create methods on the geb page that encapsulate the overall 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 the OOP point of view, this is the best solution since the login procedure is applicable only to the login page. It makes no sense to use a module, because no other page has a login window (if this is not the case, then the modules make sense.)

It also makes no sense to use inheritance, you will get an unorganized bunch of methods and class names, such as "BaseSpec", bleh.

0
source share

All Articles