Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test grails with domain objects using GORM functions

I have the following piece of code inside a service class named OrderService in Groovy on Grails. I want to make a unit test for this class. User and Order are domain classed. A user has many orders.

boolean testfun(long userId, lond orderId){

        User user = User.findByUserId(userId)
        if(user == null)return false
        Order order = Order.findByUserAndId(user, orderId)
        if(order == null)return false

        return true
    }

The unit test that I am trying to write is the following (using Spock):

@TestFor(OrderService)
@Mock([User, Order])
class OrderServiceSpec extends Specification{
 def "test funtest"() {

        User user = new User(2)
        Order order = new Order()
        order.metaClass.id = 3// I want to assign the id of the order in domain  
        order.save()        
        user.addToOrders(order)
        user.save()

        expect:
        service.testfun(2,3) == true
}
}

However this test fails because the order is null. Can anyone help me? Another question is: is this test a unit test? or should I write an integration test in grails?

like image 553
Ectoras Avatar asked Jan 26 '16 15:01

Ectoras


1 Answers

It depends on what you're actually trying to test, but this can be a unit test—I'd just recommend modifying it a little bit to isolate only the service method that you're interested in testing. You're not looking to test the domain classes at all, so it's best to mock/stub the behavior that you need from them to test the service functionality.

A good way to do this is with Spock's support for interaction based testing via mock objects. Basically we specify that when the service's testfun() method is called, we expect User.findById() to be called once and Order.findByUserAndId() to be called once as well. Spock then allows us to stub out each method call so that we specify what we want the method to return. When we run the test, the stub will be used, not the actual GORM method.

Some complexity lies with stubbing out static methods (like GORM methods), but you can use a GroovySpy to get the job done.

Also, I'm assuming you meant to use User.findById() instead of User.findByUserId()?

Something along these lines should work for you:

def "test funtest"() {
    setup:
    // Global so that it replaces all instances/references of the
    // mocked type for the duration of the feature method.
    GroovySpy(User, global: true)
    GroovySpy(Order, global: true)

    when:
    def result = service.testfun(2,3)

    then:
    // No need to return real objects, so use a mock
    1 * User.findById(2) >> Mock(User)
    1 * Order.findByUserAndId(_ as User, 3) >> Mock(Order)
    result == true

    when:
    result = service.testfun(2,3)

    then:
    1 * User.findById(2) >> null
    result == false
}

Note that we've isolated the service method. Any collaborating objects (User and Order) are only being interacted with via stubs and we can test the functionality of the service method without worrying about GORM at all.

like image 54
dpcasady Avatar answered Oct 14 '22 05:10

dpcasady