Skip to content
Jan Machacek edited this page Jul 2, 2012 · 1 revision

#Inserting test data

For a lot of the tests that you will be writing, you will need insert data to support your tests. You have several options; you can either:

  1. Use pre-populated test database,
  2. Write scripts that will insert the required test data,
  3. Use your application's repository code to insert the test data.

Now, each approach has its advantages and drawbacks. Using pre-populated database means that each developer needs to maintain his or her own copy of the database. In case of writing scripts, we are faced with scripts that are disconnected from the rest of our application's code. Finally, using the persistence tier in our application in the tests is cumbersome.

You can use the HibernateDataAccess, HibernateTemplateDataAccess, (and the future JpaDataAccess,SqlDataAccess, DocumentDataAccess or GraphDataAccess) traits together with the BeanTables trait to create the test data in a convenient tabular manner that is half-way between writing scripts that are completely disconnected and creating the objects manually and using your repository objects to persist them.

In other words, Specs2 Spring gives us half-way house that combines the scripts and using the repository tier to insert the objects. You can combine the script-like approach with manually creating and inserting the records. Specs2 Spring comes with BeanTables, a trait that allows you to construct lists of objects with their properties written in a convenient tabular form.

"username" | "firstName" |
"janm"    !! "Jan"       |
"marco"   !! "Marc"

Assuming there is a persistent class User, defined as:

@Entity
case class User() {
  @Id
  @GeneratedValue
  @BeanProperty
  var id: Long = _
  @Version
  @BeanProperty
  var version: Int = _
  @BeanProperty
  var username: String = _
  @BeanProperty
  var firstName: String = _
  @BeanProperty
  var lastName: String = _

}

We would now expect that this table somehow creates two User instances whose username and firstName properties are set to the values in the rows of the table. We now need to be able to obtain the created objects or--as we'll see later--insert them into some underlying persistence mechanism. The "table" object that BeanTables creates contains functions |> and |<. Their names hint at the direction in which the objects will flow.

Let's start with obtaining the objects into a List. The |> function sends the rows of the constructed objects towards the left and top; that is, towards the variable definition.

class SpecificationSpec extends Specification with BeanTables {

  "user objects using BeanTables:" in {
    val inferredUsers: List[User] =
      "username" | "firstName" |
      "janm"    !! "Jan"       |
      "marco"   !! "Marc"      |<

    val typedUsers =
      "username" | "firstName" |
      "janm"    !! "Jan"       |
      "marco"   !! "Marc"      |<classOf[User]

    // assert something about typedUsers and inferredUsers
  }
}

The two variables, inferredUsers and typedUsers show the application of the |> function. In the first case, the function's type is inferred from the type of the inferredUsers variable. In the second case, we supply the type of the elements in the list (and we don't need to explicitly specify the type of the List). Once you have the instances from the table, you can then use them as any other instance in your code.

In a lot of cases, though, you will want to insert the data using some underlying persistence mechanism. To do that, you need to use the |> functions of the table. The |> function expects a function that takes some type B and returns Result. Specs2 Spring includes traits with functions that return just the function that |> expects. For example, you can use the insert function from the HibernateDataAccess trait.

class SpecificationSpec extends Specification
  with BeanTables with HibernateDataAccess {

  "user objects using BeanTables:" in {
    val sessionFactory = make-hibernate-SessionFactory()

    "username" | "firstName" |
    "janm"    !! "Jan"       |
    "marco"   !! "Marc"      |> insert[User](sessionFactory)

    // assert something about the users in the DB
  }
}

We will explore the details of the traits with functions that can be used in the |> function in the table.

##HibernateDataAccess The HibernateDataAccess trait includes functions insert and deleteAll; functions that can be used with the BeanTable's |> functions.

We have the insert[T](implicit sessionFactory: SessionFactory): (T => Result) and insert[T, R](f: T => R)(implicit sessionFactory: SessionFactory): (T => Result). The first insert returns a function that takes an instance T and performs the Hibernate saveOrUpdate operation. The second insert function takes a function that is applied to every instance T before it is inserted. Both functions accept the implicit parameter of type SessionFactory.

###insert[T](implicit sessionFactory: SessionFactory): (T => Result) The first insert requires only the SessionFactory that will be used to perform the saveOrUpdate operation. In a typical Spring Framework application, the SessionFactory is usually a Spring bean. Therefore, it can be injected into the specification.

@ContextConfiguration(Array("classpath*:/META-INF/spring/module-context.xml"))
class SpecificationSpec extends Specification
  with BeanTables with HibernateDataAccess {

  @Autowired implicit var sessionFactory: SessionFactory = _
  @Autowired var springComponent: SpringComponent = _

  "springComponent must:" in {
    "username" | "firstName" |
    "janm"    !! "Jan"       |
    "marco"   !! "Marc"      |> insert[User]

    springComponent.findAll[User].size() must_== (2)
  }
}

###insert[T, R](f: T => R)(implicit sessionFactory: SessionFactory): (T => Result)

The usage of the second insert function requires a function that will operate on every instance of type T from the table (with its properties set) and the SessionFactory. Again, in a typical Spring Framework application, the SessionFactory is usually a Spring bean. Therefore, it can be injected into the specification.

@ContextConfiguration(Array("classpath*:/META-INF/spring/module-context.xml"))
class SpecificationSpec extends Specification
  with BeanTables with HibernateDataAccess {

  @Autowired implicit var sessionFactory: SessionFactory = _
  @Autowired var springComponent: SpringComponent = _

  "springComponent must:" in {
    "username" | "firstName" |
    "janm"    !! "Jan"       |
    "marco"   !! "Marc"      |> insert[User]

    springComponent.findAll[User].size() must_== (2)
  }
}
Clone this wiki locally