Mock Server
guides
Stateful Response

Stateful responses

From a development and testing perspective, being able to explore a service's behaviour is significantly more valuable than dealing with canned responses. To emulate a service's behaviour we need to respond to requests dynamically and this requires persisting data. Mock Server provides a global state object for storing and retrieving data. We're going to extend our users service to emulate real behaviour by persisting users and performing operations on it. Let's start by reworking the create user service:

// create state.users if it doesn't exist
state.users = state.users || []
 
mock.define('/users', 'POST', function(req, res) {
  // validate username is present
  if (req.body.username === undefined) {
    return res.json(400, { status: "error", details: "Missing username" })
  }
 
  state.users.push(req.body)
 
  return res.json({status: "ok"})
})

As a convenience, we initialise state.users before defining any routes so it is always accessible. The route will store the user details with username as the lookup key. Changing our other routes to retreive users is trivially simple. Lets also add basic error handling, for example if a user isnt found we'll return a 404 response:

mock.define('/users', 'GET', function(req, res) {
    if (req.query.age) {
      // convert req.query.age from String to a Number before comparing
      return res.json(_.filter(state.users, { 'age': Number(req.query.age) }))
    }
  
    return res.json(state.users)
  })
  
  mock.define('/users/{username}', 'GET', function(req, res) {
    var user = _.find(state.users, { 'username': req.params.username })
    
    // respond with 404 if user isn't found
    if (!user) { 
      return res.json(404, { error: { message: 'User doesnt exist' } }) 
    }
    
    return res.json(user)
  })

Posting a JSON request to our updated /users route with a payload of {"username": "dave", "age": 32} will now create and persist the user for use in later transactions. This allows us to build up usage scenarios to explore the behaviour of the service..

Lets add a delete route to our users service. We'll use the Lodash _.reject function to remove the user from state.users and if the user doesn't exist a 404 will be returned with a custom error message:

mock.define('/users/{username}', 'DELETE', function(req, res) {
    var user = _.find(state.users, { 'username': req.params.username })
      
    if (!user) {
        return res.json(404, { error: { mesage: 'User doesnt exist'} })
    }
    
    // use Lodash reject to remove the user
    state.users = _.reject(state.users, { 'username': req.params.username })
    
    return res.json({status: 'ok'})
  })

To round out our users service we'll add an update route. We'll update user with the details passed in req.body using the LoDash _.merge function:

mock.define('/users/{username}', 'PUT', function(req, res) {
    var user = _.find(state.users, { username: req.params.username })
      
    if (!user) { 
      return res.json(404, { error: { message: 'User doesnt exist' } }) 
    }
      
    // update the user object
    _.merge(user, req.body)
    
    // drop the user and subsequently readd
    state.users = _.reject(state.users, { username: req.params.username })
    state.users.push(user)
    
    return res.json({ status: 'ok'})
  })

At this point we have a functional users service - we can create, get, update and delete users and persist the state of our users between transactions.