Question

Im not sure how to word what I want to do so I apologise in advance

I'm currently writing unit tests for some legacy code as part of my job. One of these bits of code is a (Java) HttpServlet which provides a re-direct URL. Essentially the code looks like this:

public class Server extends HttpServlet {
  ...
  public void doGet(HttpServletRequest request, HttpServletResponse response) {
    ...
    String url = "....";
    .. // do some work to create that url
    response.sendRedirect(url);
  }
  ...
}

As part of my unit test for this class I want to test that redirect URL created. So I would do something along the lines of:

 Server server = new Server();
 ..
 server.doGet(request, response);
 String url = getRedirectURL(response);
 // test URL returned
 ...

What's the best way to 'getRedirectURL' from the response?

I'm currently using Junit and Jmock (both latest versions). I create a Mock HttpServletRequest and I can easily create a mock HttpServletResponse and add an expectation for the sendRedirect(url) method IF I knew the expected format of the URL, however I do not.

My attempt was to create a mock class, implementing HttpServletResponse which stored the value of the url and then allowed me to retrieve it later. i.e.

  private class MockHttpServletResponse implements HttpServletResponse {
  String url;
  public String getURL() {
     return url;
  }
  public void sendRedirect(String url) {
     this.url = url;
  }
 }

However, because it implements an interface Java wants me to implement ALL the methods in HttpServletResponse which I really don't want to do.

Thank you in advance :) i'll try and re-word this

Was it helpful?

Solution

You need to implement a custom matcher. See Writing New Matchers

A custom matcher allows you to test exactly what you want, but it will also allow you to store the value that is passed to the matcher, so that you can retrieve it later:

From the jmock site:

import org.hamcrest.AbstractMatcher;

public class StringStartsWithMatcher extends TypeSafeMatcher<String> {
    private String prefix;
    private String calledValue; // ADDED THIS

    public StringStartsWithMatcher(String prefix) {
        this.prefix = prefix;
    }

    public String getCalledValue() {
        return calledValue;
    }

    public boolean matchesSafely(String s) {
        this.calledValue = s; // AND HERE
        return s.startsWith(prefix);
    }

    public StringBuffer describeTo(Description description) {
        return description.appendText("a string starting with ").appendValue(prefix);
    }
}

@Factory
public static Matcher<String> aStringStartingWith( String prefix ) {
    return new StringStartsWithMatcher(prefix);
}

public class MyTestCase {
    ...

    public void testSomething() {
        ...

        TypeSafeMatcher<String> matcher = aStringStartingWith("http")

        context.checking(new Expectations() {{
            oneOf (logger).error(with(matcher));
        }});

        // more tests using matcher.getCalledValue()
        ...
    }
}

So you're storing the url value in calledValue, which you're then retrieving through getCalledValue().

OTHER TIPS

I'm guessing you just want to confirm that the sendRedirect method was invoked on the response mock but you don't care what the value was. If that's the case you can set an expectation like this:

response.sendRedirect(with(a(String.class)));

Given this is all taking place in a unit test, can you not predict what the URL will be and so match it? That said, it's often worth using matchers to pull out just the features that you care about, which makes the test more focussed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top