3

I'm creating a Rest API with spray-routing on top of mongodb for some CRUD operations, this all works fine, expect whenever I try to test it with specs2 the following specification

class RestServiceSpec extends Specification with Specs2RouteTest with RoutingRestService

  // database initialization removed for clarity

  "The rest service" should
    "have a player called 'Theo TestPlayer' in the db" in {
      Get("/api/1.0/player/" + player1._id) ~> restRoute ~> check {
        entityAs[Player] must be equalTo(player1)
      }
    }
  }

// some more specs removed for clarity
}

it will fail with the following error:

MalformedContent(invalid ObjectId ["51308c134820cf957c4c51ca"],Some(java.lang.IllegalArgumentException: invalid ObjectId ["51308c134820cf957c4c51ca"])) (Specs2Interface.scala:25)

I have no idea where to look as the reference to the source file and line number point to a generic failTest(msg:String) method

some more info:

I have a case class that I persist to Mongo using SalatDAO

case class Player(@Key("_id") _id:ObjectId = new ObjectId(), name:String, email:String, age:Int) {}

where ObjectId() a class is that wraps mongodb's ID generation to get this (un)marshalled through spray_json I created some jsonFormats

object MyJsonProtocol {
  implicit val objectIdFormat = new JsonFormat[ObjectId] {
    def write(o:ObjectId) = JsString(o.toString)
    def read(value:JsValue) = new ObjectId(value.toString())
  }
  implicit val PlayerFormat = jsonFormat(Player, "_id", "name", "email", "age")

and the relevant part of my route (removed error handling and logging):

  path("player" / "\\w+".r) {id:String =>
    get {
      respondWithMediaType(`application/json`) {
        complete {
          PlayerCRUD.getById(id) 
        }
      }
    } ~
Gertjan Assies
  • 1,890
  • 13
  • 23

2 Answers2

3

As nobody seems to know, I changed the _id from being a ObjectId() to just a string, and having a helpermethod to create it from new ObjectId().toString where needed

Gertjan Assies
  • 1,890
  • 13
  • 23
  • 1
    Thank you. I had the same problem and I managed to solve it by reading your question and an answer :) – Marcin Jun 29 '14 at 21:20
1
 implicit object ObjectIdJsonFormat extends JsonFormat[ObjectId] {
    def write(obj: ObjectId): JsValue = JsString(obj.toString)

    def read(json: JsValue): ObjectId = json match {
      case JsString(str) => new ObjectId(str)
      case _ => throw new DeserializationException(" string expected")
    }
  }
Robin
  • 6,879
  • 7
  • 37
  • 35
  • spray-json formatters are build around the ability to compose your formatters, and it already has formatters for all the default types, so only formatters for the types it doesn't support should be necessary – Gertjan Assies Sep 08 '14 at 15:06