I’m an advocate of TDD (test driven development) and I try too use it all the time when I’m coding. I have gotten accustomed to using JMock for a lot of my testing as most of what I test are servlets and such which require quite a bit of setup (HttpServletResponse, HttpServletRequest etc). The one thing I don’t like though is that using jmock exposes too much of the inner workings (or expectations of the inner workings) of the system to be flexible enough for rapid design. What I mean by this is that when I’m coding TDD style, I write my test case first and as I do I am thinking about how I may setup the class interface etc… Jmock forces me to setup certain expectations in advance (not with a gun to my head but pretty close) about how the code will actually implement the solution.
For example say I’m writing a login servlet called LoginServlet. This servlet class has a method called “doPost” that takes a ServletRequest and ServletResponse. My test case begins like this…
public void testLoginWithValidCredentials()throws Exception{ String email = "testmonkey@nodomain.com";String pass = "abc123"; StubRequest req = new StubRequest(); req.setParameter("email",email); req.setParameter("password",pass); req.setSession((HttpSession)mockHttpSession.proxy()); .... .... loginServlet.doPost(req,response); }
Now I knew in advance that I was going to need a session object because I’m putting something on it after the login; what that is and how it works are unknown at the moment. Also at this point I don’t really have any expectations for my test case. I guess my expectation is that after executing loginServlet.doPost(…) that there will be some new information on the Session object. Thats about all I know to be true. So we can pull the session off the request object afterward and check that its there…. or using the JMock way we can set an expectation that the mockHttpSession receives a setAttribute message once with some data (At this point I have decided that I will store an Account object on the session). So we create our JMock expectation:
mockHttpSession.expects(once()).method("setAttribute").with(eq(Account.class.getName()),eq(account));
**Note: I created an account instance object with some test data
Great, now we can know for sure that the info was set to the session. But how to we get that info?
All of a sudden I need to make all sorts of assumptions about how the code is going to be implemented instead of actually implementing it. I’m sure I’m just missing something here but if the way I get the data is by creating a LoginController instance inside the servlet then I need a way to control what comes out of that controller from the test case. Ok, so I could add an overloaded constructor to my Servlet that takes a LoginController instance and then store that inside the servlet for use during the doPost call… my gut tells me that may be the wrong thing to do. Lets try it out and make sure….
public void testLoginWithValidCredentials() throws Exception{ LoginControllerStub loginControllerStub = new LoginControllerStub(); loginControllerStub.setAccount(account); loginServlet = new LoginServlet(); loginServlet.setLoginController(loginControllerStub); String email = "testmonkey@nodomain.com"; String pass = "abc123"; StubRequest req = new StubRequest(); req.setParameter("email",email); req.setParameter("password",pass); req.setSession((HttpSession)mockHttpSession.proxy()); mockHttpSession.expects(once()).method("setAttribute").with(eq(Account.class.getName()),eq(account)); loginServlet.doPost(req, response); }
Now we need to fill in the login servlet code…
public class LoginServlet extends HttpServlet { private LoginController loginController = null; public void setLoginController(LoginController lc){ this.loginController = lc; } protected LoginController getLoginController(){ if(this.loginController==null){ this.loginController = new LoginController(); } return this.loginController; } @Override public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String email = req.getParameter("email"); String password = req.getParameter("password"); LoginController loginController = getLoginController(); Account account = loginController.getAccount(email, password); if(account!=null){ // successful login req.getSession().setAttribute(Account.class.getName(), account); }else{ // something was wrong with the creds } } }
And the test passes…. Hmmm maybe there is a clean way to do this after all….
The power of blogging wins again. This is really just a ramble run amok, if you have comments please send them to me.
Image may be NSFW.
Clik here to view.
Clik here to view.
Clik here to view.
Clik here to view.
