-
Notifications
You must be signed in to change notification settings - Fork 11
TestData
#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:
- Use pre-populated test database,
- Write scripts that will insert the required test data,
- 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)
}
}