////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

https://spockframework.org/spock/docs/[Spock] is the recommended tool for writing unit tests with GORM and is trivial to setup.

==== GORM with Hibernate and Spock Basics

The following is an example Spock unit test:

[source,groovy]
----
import spock.lang.*
import grails.gorm.annotation.Entity
import org.grails.orm.hibernate.HibernateDatastore

class ExampleSpec extends Specification { <1>

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore <2>

    void setupSpec() {
       hibernateDatastore = new HibernateDatastore(Person) <3>
    }

    void "test something"() { <4>
       // your logic here
    }
}

@Entity <5>
class Person {
    ...
}
----

<1> The test should extend `spock.lang.Specification`
<2> The `Shared` annotation is used to indicate to Spock that the `HibernateDatastore` is shared across all tests. The `AutoCleanup` annotation makes sure that `HibernateDatastore` is shutdown when all tests finish executing.
<3> Within the `setupSpec` method a new `HibernateDatastore` is constructed with the classes to use as the argument to the constructor.
<4> You then write your test logic within each method
<5> You can inline domain classes within the unit test if you annotate them with `@Entity`

==== Spock and Transactions

Note that in general you have to wrap your test execution logic in a session or transaction. The easiest way to do this is with `grails.gorm.transactions.Transactional`:

[source,groovy]
----
...
import grails.gorm.transactions.*
import org.springframework.transaction.PlatformTransactionManager

class ExampleSpec extends Specification {

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore
    @Shared PlatformTransactionManager transactionManager <1>

    void setupSpec() {
        hibernateDatastore = new HibernateDatastore(Person)
        transactionManager = hibernateDatastore.getTransactionManager() <2>
    }

    @Transactional <3>
    def setup() {
        new Person(firstName:"Fred").save()
    }

    @Rollback <4>
    void "test execute GORM standalone in a unit test"() {
        // your logic here
    }
}
----

<1> The `PlatformTransactionManager` is defined as a `Shared` field
<2> You can obtain the `PlatformTransactionManager` from the `HibernateDatastore`
<3> The `Transactional` annotation is used to setup test data
<4> The `Rollback` annotation is used to rollback any changes made within each test

In the example above, each test method is wrapped in a transaction that rolls back any changes using the `grails.gorm.transactions.Rollback` annotation.


TIP: If you want to setup some test data within the `setupSpec` method that is shared across all tests then you can use `withTransaction`:


[source,groovy]
----
...
void setupSpec() {
    hibernateDatastore = new HibernateDatastore(Person)
    ...
    Person.withTransaction {
        new Person(firstName:"Fred").save()
    }
}
----

==== Configuring GORM in Spock

If you need to configure GORM within a Spock unit test you can pass a map to the constructor of `HibernateDatastore`. For example to setup multi-tenancy:

[source,groovy]
----
...
void setupSpec() {
    Map configuration = [
        'grails.gorm.multiTenancy.mode':'DISCRIMINATOR',
        'grails.gorm.multiTenancy.tenantResolverClass':SystemPropertyTenantResolver
    ]
    hibernateDatastore = new HibernateDatastore(configuration, Person)
    ...
}
----
