Grails
  1. Grails
  2. GRAILS-8294

Service test using domain object with ref to spring security service fails

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Not A Bug
    • Affects Version/s: 2.0-RC1
    • Fix Version/s: None
    • Component/s: Testing
    • Environment:
      MacBook Pro OS X 10.6 with Java 1.6
    • Testcase included:
      yes

      Description

      I am writing a unit test for a service that I created which uses the Spring Security Core default User domain object which uses springSecurityService auto-wired. Here is the basic test code:

      @TestFor(RegisterService) 
      @Mock(User) 
      class RegisterServiceTests { 
      
          @Test 
          void shouldCreateNewAccount() { 
              def userCreated = service.createNewAccount(data) 
      
              assert User.count() == 1 
          } 
      } 
      

      This fails because of the following:

      java.lang.NullPointerException: Cannot invoke method encodePassword() on null object 
      

      Which points to default User domain object line that is:

      protected void encodePassword() { 
         password = springSecurityService.encodePassword(password) 
      }
      

      How can I auto-wire or set the User domain object variable in this unit test:

      transient springSecurityService
      

      Additional Information:

      I found out some more when I tried some different searches for other issues I was seeing. Here is a page on how to work in controller unit tests to get something similar working:

      http://blog.block-consult.com/2011/08/

      I tried this approach, as well, and it almost works in my controller that is using the service. After it has successfully used the service from the controller, I try to assert User.count() == 1 and it throws another exception on the encodePassword method as before. Why would the beforeInsert method on the User domain object get called even when asking for count()? I tried User.findByUsername(...) and it did the same thing?

      When I tried the approach described in the article for the service it did not work. In fact, there is a comment on the article where a user had the same problem that I did:

      http://blog.block-consult.com/2011/08/inject-spring-security-service-into-domain-class-for-controller-unit-testing/#comments

      This problem originally was listed on grails-user mailing list:

      http://grails.1312388.n4.nabble.com/Service-test-using-domain-object-with-ref-to-spring-security-service-in-Grails-2-0M2-td3926624.html

        Activity

        Hide
        Peter N. Steinmetz added a comment -

        Just pushed a defineBeans branch which shows that when using the defineBeans approach from blog.block-consult.com as above, the applicationContext contains a springSecurityService bean, but that it isn't being used to populate the transient springSecurityService of the mocked Person object.

        Show
        Peter N. Steinmetz added a comment - Just pushed a defineBeans branch which shows that when using the defineBeans approach from blog.block-consult.com as above, the applicationContext contains a springSecurityService bean, but that it isn't being used to populate the transient springSecurityService of the mocked Person object.
        Hide
        Peter N. Steinmetz added a comment -

        Also pushed a 'addGetServiceToPerson' branch which shows that adding a get method through the metaClass doesn't help.

        This may be due to the fact that the transient springSecurityService bean is already defined on the class and encodePassword method directly accesses the field.

        Show
        Peter N. Steinmetz added a comment - Also pushed a 'addGetServiceToPerson' branch which shows that adding a get method through the metaClass doesn't help. This may be due to the fact that the transient springSecurityService bean is already defined on the class and encodePassword method directly accesses the field.
        Hide
        Peter N. Steinmetz added a comment -

        Added 'MockEncodeOnPerson' branch, which shows that mocking encodePassword directly on the Person class works, but of course, it doesn't call springSecurityService.

        Show
        Peter N. Steinmetz added a comment - Added 'MockEncodeOnPerson' branch, which shows that mocking encodePassword directly on the Person class works, but of course, it doesn't call springSecurityService.
        Hide
        Gregor Petrin added a comment -

        I hit this problem in 2 of my apps recently and it is very frustrating

        Show
        Gregor Petrin added a comment - I hit this problem in 2 of my apps recently and it is very frustrating
        Hide
        Charles Bernasconi added a comment -

        Using the solution suggested by Peter, by mocking the encodePassword method on the Person class works. However, I inherit from the Person class. Unfortunately when using the sub-class, the original encodePassword of the Person class is called, despite the mocking. The only way around this that I have found is have the Person class implement GroovyInterceptable - not a great workaround. Therefore I would really like a proper solution that allows a mock SpringSecurityService to be injected into the Person class

        Show
        Charles Bernasconi added a comment - Using the solution suggested by Peter, by mocking the encodePassword method on the Person class works. However, I inherit from the Person class. Unfortunately when using the sub-class, the original encodePassword of the Person class is called, despite the mocking. The only way around this that I have found is have the Person class implement GroovyInterceptable - not a great workaround. Therefore I would really like a proper solution that allows a mock SpringSecurityService to be injected into the Person class

          People

          • Assignee:
            Unassigned
            Reporter:
            Chris Sterling
          • Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development