czwartek, 14 listopada 2013

Partial mocking and spying with Spock

I think that everyone sooner or later gets to the point where the setup of the test class becomes ridicoulous comparing to the test methods. I recently had to update some GWT servlet class, which was missing JUnit tests. So I started with standard way of mocking out external dependencies, but it quickly came out that not that simple...

My servlet class was extending some external servlet class, and was using the parent method to obtain logged in user login to perform verification if user can do an action.
It looked like this

 
public class SessionDataProviderService extends RemoteServiceServlet {



     public String getSessionUserLogin() {

        return (String) this.getThreadLocalRequest().getSession().getAttribute(SecurityFilter.SESSION_USER_LOGIN);

    }

}



My class

 
public class ProcessService extends SessionDataProviderService{

    IProcessInterface getProcessInterface() {
        WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        return (IProcessInterface ) wac.getBean("processServiceBean");
    }

    IUserInterface getUserInterface() {
        WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        return (IUserInterface) wac.getBean("userInterface");
    }
    public Boolean cancelProcess(final Long processId) {
        if (canUserPerformAction()) {
            return getProcessInterface().cancelOne(id);
        } else {
            return false;
        }
    }

    @Override
    public boolean canUserPerformAction() {
        return getUserInterface().isUserInRole(getSessionUserLogin(), Role.PROCESS_MANAGER)

    } 

}


So, as you can see, I need to mock out the IUserInterface and IProcessInterface. They're provided by spring web application context, so I needed to mock the static WebApplicationContextUtils, then create mocks for my beans, wire the mocking parts... So quite a lot of work just to test 2 methods. And there are also problems with getSessionUserLogin(). It uses the getThreadLocalRequest() from GWT RemoteServiceServlet, which was not accesible for me. So I started to get NPE when this method was invoked. I was not really interested in user login, it was not relevant to my case. So I decided to go for partial mock of my class. And this also helped me to lower down the mocking code of IUserInterface and IProcessInterface to minimum. Here is the code

 
class ProcessServiceTest extends Specification{



    Long processId = 1L

    IProcessInterface processInterface = Mock()

    IUserInterface userInterface = Mock()





    def "verify that nonauthorized users cannot cancel process"(){

        given:

        ProcessService service = Spy()

        when:

        service.cancelProcess(processId )

        then:

        1*service.canUserPerformAction() >> false

        0*processInterface.cancelOne(processId)

    }



    def "verify that authorized users can cancel process"(){

        ProcessService service = Spy(ProcessService ) { getProcessInterface() >> processInterface }

        when:

        service.cancelProcess(processId)

        then:

        1*service.canUserPerformAction() >> true

        1*processInterface.cancelOne(processId)

    }

    

    def "verify that only process manager can do actions"(){

        ProcessService service = Spy(ProcessService ) { getUserInterface() >> userInterface }

        when:

        service.canUserPerformAction()

        then:

        service.getSessionUserLogin() >> "test"

        1*userInterface.isUserInRole("test", RoleType.PROCESS_MANAGER)

     }

}



So, as you can see, not much mocking here, nothing related to spring context, servlet context etc.
Spy() from Spock nicely generates the object, and calls real methods unless they're specified as interaction. So in first test method  we generate standard spy, and just mock the canUserPerformAction() method to return false.
In second test, we need the mock, since we assume it will be invoked. So the

Spy(ProcessService ) { getProcessInterface() >> processInterface }

does just that. It allows to redefine a method at Spy creation time. Then all is left is to verify the calls.

Brak komentarzy:

Prześlij komentarz