Dan Hiester
Dan Hiester

Reputation: 43

Why does a java.util.Date object satisfy one Specs test but fail another?

I'm trying to test a model method using Play Framework 2.0 and Specs2. Global.scala populates the DB on first run with data. In one test, I can successfully test it using code like below:

def dateHelper(str: String): Date = new SimpleDateFormat("MM/dd/yyyy").parse(str)

"Food model" should {
  "be retrieved by id" in {
    val Some(mashedPotatoes) = Food.findById(1000)

    mashedPotatoes.name must equalTo("Mashed Potatoes")
    mashedPotatoes.eaten must equalTo(false)
    mashedPotatoes.id must equalTo(Id(1000))
    mashedPotatoes.owner must equalTo(Id(1))
    mashedPotatoes.expiry must equalTo(dateHelper("05/21/2012"))
  }
}

That test passes. But if I try to select more than one item from the model, and test it as a list:

"return food for test user in " in {

  running(FakeApplication()) {
    val testFoods: Seq[Food] = Food.findFoodFor(Id(1)) // Test user's ID is 1

// This test fails        
    testFoods must equalTo(
      List(
        Food(Id(1001), "Fried Green Tomatoes", false, Id(1), dateHelper("04/21/2012")),
        Food(Id(1000), "Mashed Potatoes", false, Id(1), dateHelper("05/21/2012"))
      )
    )

// This test passes
    testFoods.head.expiry must equalTo(dateHelper("04/21/2012"))
  }
}

The error output tells me that the date fields are not equal:

[error] x return food for test user in
[error]     'Food(1001,Fried Green Tomatoes,false,1,2012-04-21 00:00:00.0), Food(1000,Mashed Potatoes,false,1,2012-05-21 00:00:00.0)' is not equal to 'Food(1001,Fried Green Tomatoes,false,1,Sat Apr 21 00:00:00 EDT 2012), Food(1000,Mashed Potatoes,false,1,Mon May 21 00:00:00 EDT 2012)' (ModelSpec.scala:66)
[error] Expected: ...se,1,[Sat Apr ]21...00[ EDT 2]0[12]),...1,[Mon May ]21...00[ EDT 2]0[12])
[error] Actual:   ...se,1,[2012-04-]21...00[.]0[]),...1,[2012-05-]21...00[.]0[])

Is there something I'm missing, here?

Edit: It looks like it was the DB schema, which had set the expiry column to be of type timestamp, instead of type date.

More helpful-looking info here: java.util.Date vs java.sql.Date

Upvotes: 4

Views: 645

Answers (1)

leedm777
leedm777

Reputation: 24032

The problem is likely because of how the data is loaded from the database. I bet it's using java.sql.Timestamp instead of java.util.Date.

scala> val date = dateHelper("05/12/1974")
d: java.util.Date = Sun May 12 00:00:00 CDT 1974

scala> val dbDate = new java.sql.Timestamp(d.getTime)
dbDate: java.sql.Timestamp = 1974-05-12 00:00:00.0

scala> date == dbDate
res6: Boolean = true

scala> dbDate == date // prepare to be amazed!
res5: Boolean = false

If you read the fine print in the Javadoc, you'll find this nice little statement:

Due to the differences between the Timestamp class and the java.util.Date class mentioned above, it is recommended that code not view Timestamp values generically as an instance of java.util.Date. The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

There may be something wrong in your ORM mapping, or your database schema. Is the column a DATE or TIMESTAMP or TIMESTAMP WITH TIMEZONE? If it's DATE, you ought to map that to java.sql.Date, which should compare correctly. If it's TIMESTAMP WITH TIMEZONE, then your dateHelper ought to build Timestamps. If it's TIMESTAMP, good luck, since the database will have lost timezone information.

Upvotes: 4

Related Questions