How to unit test controllers that use springSecurityService?

后端 未结 4 1351
傲寒
傲寒 2021-02-08 19:05

I have a user class like such:

class User {
    transient springSecurityService
    String displayName
    String password

    protected void encode         


        
相关标签:
4条回答
  • 2021-02-08 19:39

    I personally don't like adding logic to production code to help satisfy a test. Sometimes you have to make a decision on what's best to do. Couple of options...

    1. The above answer will work, but as I stated I personally wouldn't prefer
    2. Don't unit test. Write all of your tests that run into this situation as integration tests.
    3. Mock it out with a fake service.

    If this code (or code that runs into the same problem) is sprinkled throughout your application then you'd probably want to figure out a way to mock out these calls in your unit tests for all test cases so you're not duplicating your setup efforts everywhere. An easy way to mock this out is with metaClassing.

    @Test
    public void something() {
       def user = ...
       def springSecurityService = new Object()
       springSecurityService.metaClass.encodePassword = {String password -> "ENCODED_PASSWORD"}
       user.springSecurityService = springSecurityService
       ...
    }
    

    Now when the springSecurityService.encodePassword gets invoked it should return "ENCODED_PASSWORD". I also create an Object instead of instantiating a new SpringSecurityService because if you instantiate an actual service then you may end up calling actual methods on that service unexpectedly and unknowingly and having your tests pass for the wrong reasons. I'd rather get a no such method error than a passing test that shouldn't be passing.

    0 讨论(0)
  • 2021-02-08 19:48

    I've done that like this:

        protected void encodePassword() {
            // SpringSecutiryService is not injected in tests.
            if (springSecurityService)
                password = springSecurityService.encodePassword(formPassword)
        }
    
    0 讨论(0)
  • 2021-02-08 19:49

    In my case I tried to override SecUser's encodePassword() implementation -which calls springSecurityService.encodePassword().

    I was surprised because I needed to override the class and the instance (if I didn't override any, it fails):

    SecUser.metaClass.encodePassword = { 'a' }
    user.metaClass.encodePassword = { 'b' }
    

    any idea of why do I need this?

    0 讨论(0)
  • 2021-02-08 19:59

    I think the right thing to do is to mock out the service. You'll want to test the different cases that can be the return value and that the correct value is passed to the service method.

    @Test
    public void something() {
       def user = ...
       def expectedPassword = 'mock encoded pass' 
       controller.springSecurityService = [encodePassword: { String passwd -> return expectedPassword }]
    
       ...
    }
    

    or

    @Test
    public void something() {
       def user = ...
       def expectedPassword = 'mock encoded pass' 
       def mockSecurityService = mockFor(SpringSecurityService)
       mockSecurityService.demand.encodePassword { String passwd -> return expectedPassword}
       controller.springSecurityService = mockSecurityService.createMock()
    
       ...
       mockSecurityService.verify() // throws exception if demands aren't met
    }
    
    0 讨论(0)
提交回复
热议问题