Search code examples
cookieswhere-clausespockgeb

Spock test in GEB cookies using where clause


I've configured some geb tests to check a different messages depending on the login attempt in my web app. Since the message and the input fields will change at the third login attempt.

The login is a two step login based on password send to a specific phone number, so in the first page LoginPage the user introduces their Id and phoneNumber, then it's redirected to the second page ValidationLoginPage where the user introduces the received password.

I want to check that in the second page the user can only introduces three bad passwords and at fourth attempt the input to introduce a password will disappear and a different message indicating that there are no more attempts it's showed.

To check this I prepared a test which introduces the Id and phoneNumber at the given: clause, and using where: clause it introduces a bad password three times. Since where: repeat all test I try to control the part to repeat using injected variable like in where: so I've something like:

def "Test max loging attempts"(){
  given:
    if(loginAttempt == 1)
      to LoginPage
      loginModule.startLogin(cfg.user.id,cfg.user.phone)
    }
  when:
    at LoginValidationPage
    assert  $('div.box_id_header h3').text() == 'Verify your code'
    assert  $('#code').css('display').contains('block')
    loginModule.verifyPassword('WRONGPASSWORD')
  then:
    at LoginValidationPage
    println "Attempt ${loginAttempt}"
    if(loginAttempt == 4){
      // last attempt
      assert    $('#code').css('display') == 'none' 
      assert  $('#divCodeErrorMsg').text().contains('No more attempts')
    }else{
      assert    $('#code').css('display').contains('block')
      assert  $('#divCodeErrorMsg').text().contains('Wrong password. Try again.')
    }
  where:            
    loginAttempt << (1..4)
}

My problem is, that cookies are cleared for each where: iteration, thought the message and the behavior is not which I expect. I don't want to configure autoClearCookies=false in GebConfig.groovy file since I've another tests where this feature is necessary. There is a way to avoid the clear cookies for this method using spock def setupSpec() {} method and reactivate in def cleanupSpec() {} method?

Additionally it's also possible to use where: in a cleaner way avoiding to check the loginAttempt variable to avoid run given: part multiple times, or there is a better approach not using where: at all?


Solution

  • The problem is that you maybe misunderstand and thus abuse Spock's where: block. It is designed to parametrise a feature method and each run of that method comprises an independent feature. If you @Unroll your feature, even for each set of where: parameters a new method is generated. Because features should be independent of each other and theoretically be able run run in any given order or even in parallel, test fixtures need to be reset in order to run them. This is what happens with your cookies because you abuse the where: feature to implement a simple loop.

    There are other little problems in your code, such as the non-asserted at check in the when: block. Even if at yields false, it has no consequence there if you do not use assert. You can only skip assert in then: or expect: blocks, but not in given: or when: and also not inside closures or helper methods which I will both use in my sample code.

    How about this?

    package de.scrum_master.stackoverflow.foo
    
    import geb.spock.GebReportingSpec
    
    class LoginTest extends GebReportingSpec {
      def loginModule = new LoginModule()
      def cfg = new Config()
    
      def "Test max login attempts"() {
        given: "we are at the login page"
        browser.config.autoClearCookies = false
        to LoginPage
    
        when: "logging in 4x with wrong credentials"
        (1..4).each {
          loginWithWrongCredentials(it, it < 4)
        }
    
        then: "we get a 'no more attempts' error message"
        $('#codi').css('display') == 'none'
        $('#divCodiValidacioError').text().contains('No more attempts')
      }
    
      def loginWithWrongCredentials(int loginAttempt, boolean checkForWrongPasswordMessage) {
        println "Login attempt ${loginAttempt}"
        loginModule.startLogin(cfg.user.id, cfg.user.phone)
        assert at(LoginValidationPage)
        assert $('div.box_id_header h3').text() == 'Verify your code'
        assert $('#code').css('display').contains('block')
        loginModule.verifyPassword('WRONGPASSWORD')
        assert at(LoginValidationPage)
        if (checkForWrongPasswordMessage) {
          assert $('#codi').css('display').contains('block')
          assert $('#divCodiErrorMsg').text().contains('Wrong password. Try again.')
        }
      }
    }
    

    I would also recommend to move the content asserts from loginWithWrongCredentials into helper methods of LoginValidationPage where they rather belong and just call them from the test.