Mocking HTTP Requests

It is often useful in testing to simulate a full HTTP request to evaluate its effects on things like SiteMap, S.param, etc. This page will discuss one approach you can use to do this, utilizing the Lift’s new mock support, introduced in Lift 2.3.

There are two approaches to using mock requests in Lift. The first is to use the methods on net.liftweb.mockweb.MockWeb directly. These allow you to initialize either S or instances of Req based on String urls or HttpServletRequest instances . In particular, the net.liftweb.mocks.MockHttpServletRequest provides a nice mock of a servlet request with additional methods and properties to simplify testing, so we’ll be discussing its use as well here.

The second approach to unit testing with Lift is to use the net.liftweb.mockweb.WebSpec trait. The WebSpec trait extends the org.specs.Specification trait, so you can simply extend WebSpec to create a Specification with some additional support for wrapping MockWeb.

Using MockWeb Directly

MockWeb provides two main methods for unit testing, the testReq method, which constructs a net.liftweb.http.Req instance and then executes it against a Req ⇒ T function, and the testS method, which initializes the net.liftweb.http.S object and then executes a ⇒ T function. Both testReq and testS can be initialized with either a string URL or a HttpServletRequest, and testS can also take an optional net.liftweb.http.LiftSession instance so that you can share sessions and emulate multiple requests. Let’s look at some examples. Note that these examples come from the MockWebSpec.scala source file, so see that for more usage.

Testing a Req instance based on a String URL

In this example we initialize the Req with the url “http://foo.com/test/this?a=b&a=c”, and with the optional context path “/test”.


testReq("http://foo.com/test/this?a=b&a=c", "/test") {
  req => 
    req.uri must_== "/this"
    req.params("a") must_== List("b","c")
}

Testing a Req instance based on a MockHttpServletRequest

In this example we want to fine-tune the request a bit, so we construct a MockHttpServletRequest and pass that in. Note that the Servlet API specifies the context path as part of the HttpServletRequest, so you need to specify it there if you need to use it.


val mockReq = 
  new MockHttpServletRequest("http://foo.com/test/this", "/test")

mockReq.method = "POST"

import json.JsonDSL._

mockReq.body = ("name" -> "joe") ~ ("age" -> 35)

testReq(mockReq) { 
  req =>
    req.json_? must_== true
}

Testing against S with a String URL


testS("http://foo.com/test/that?a=b&b=c") {
  S.param("b") must_== Full("c")
}

Testing against S based on a MockHttpServletRequest


val mockReq = 
  new MockHttpServletRequest("http://foo.com/test/this?foo=bar", "/test")
      
testS(mockReq) {
  S.param("foo") must_== Full("bar")
  S.uri must_== "/this"
}

Sharing a session across tests

In certain cases you may want to emulate multiple requests by sharing a session across tests. he simplest approach is to return the constructed LiftSession from your first test and pass it to successive tests:


object testVar extends SessionVar[String]("Empty")
   
val session = testS("http://foo.com/test") {
  testVar("Foo!")
  S.session // returns the current session
}

// A second test
testS("http://foo.com/test2", session) {
  testVar.is must_== "Foo!"
}

Using WebSpec

The WebSpec trait has been designed to simplify writing specs against your Lift app. WebSpec extends the Specification trait and adds some additional syntactic sugar that wraps the MockWeb methods described above. The following example code is taking from the WebSpecSpec.scala source file in the Lift repo.

Testing against Templates

This section is WIP. If you’re using SBT, you’ll need to add the following to your project config so that the webapp source folder is in the classpath :

SBT 0.7.x:

override def testClasspath  = super.testClasspath +++ ("src" / "main" / "webapp")

SBT 0.10+:

unmanagedResourceDirectories in Test <+= (baseDirectory) { _ / "src/main/webapp" }

If you’re running tests from within Eclipse , add the webapp source folder in the classpath in Run → Run Configurations → Classpath. Click Advanced, Add Folder, then select the webapp folder in your project.

Testing against S

To test against an initialized S, write your spec with the withSFor method call after each expectation description. For example:


val testUrl = "http://foo.com/test/stateless"

"properly set up S with a String url" withSFor(testUrl) in {
  S.request match {
    case Full(req) => req.path.partPath must_== List("test", "stateless")
    case _ => fail("No request in S")
  }
}

Since it wraps MockWeb, the withSFor can also take a LiftSession instance, as well as a HttpServletRequest instance:


val testReq = 
  new MockHttpServletRequest("http://foo.com/test/this?foo=bar", "/test")

// Create a new session for use in the tests
val testSession = MockWeb.testS(testUrl) {
  S.session
}

object TestVar extends SessionVar[String]("Empty")

"properly set up S with a String url and session" withSFor(testUrl, testSession) in {
  TestVar("foo!")
  TestVar.is must_== "foo!"
}

"properly re-use a provided session" withSFor(testUrl, testSession) in {
  TestVar.is must_== "foo!"
}      

"properly set up S with a HttpServletRequest" withSFor(testReq) in {
  S.uri must_== "/this"
  S.param("foo") must_== Full("bar")
}

Testing against a Req instance

Testing against Req is achieved by placing the withReqFor method after your expectation description:


"properly set up a Req with a String url" withReqFor(testUrl) in {
  _.path.partPath must_== List("test", "stateless")
}

The withReqFor method can also take a contextPath:


"properly set up a Req with a String url and context path" withReqFor(testUrl, "/test") in {
  _.path.partPath must_== List("stateless")
}

as well as a HttpServletRequest:


"properly set up a Req with a HttpServletRequest" withReqFor(testReq) in {
  _.uri must_== "/this"
}

Adding a body to the request

In addition to specifying the URL or HttpServletRequest for a Req or S expectation, you can also add data to the body of the request . You may do so by using the withPost or withPut methods. Both methods set the body in addition to setting the corresponding request method . Each method is overloaded to take a:

An example of using withPost/withPut:


    "properly set a plain text body" withReqFor(testUrl) withPost("This is a test") in {
      req =>
        req.contentType must_== Full("text/plain")
        req.post_? must_== true
        req.body match {
          case Full(body) => (new String(body)) must_== "This is a test"
          case _ => fail("No body set")
        }
    }

    "properly set a JSON body" withReqFor(testUrl) withPut(("name" -> "Joe")) in {
      req =>
        req.json_? must_== true
        req.put_? must_== true
        req.json match {
          case Full(jval) => jval must_== JObject(List(JField("name", JString("Joe"))))
          case _ => fail("No body set")
        }
    }

    "properly set an XML body" withSFor(testUrl) withPost(<test/>) in {
      S.request match {
        case Full(req) => {
          req.xml_? must_== true
          req.post_? must_== true
          req.xml must_== Full(<test/>)
        }
        case _ => fail("No request found in S")
      }
    }

SiteMap and Loc with Mock Requests

Determining the proper Loc for a given Req is simple:


val currentLoc = SiteMap.findLoc(request1)