Overriding methods dynamically

In the previous recipe, Dynamically extending classes with new methods, we learned how to dynamically add a method to a class through one of the metaprogramming features of Groovy, named ExpandoMetaClass.

In this recipe, we will see how to intercept and replace a call to an existing method of a class. This technique can be very handy when writing unit tests for classes that have dependencies to other classes.

Getting ready

Let's introduce three classes for which we want to write a unit test:

  • Customer.groovy:
    package org.groovy.cookbook
    class Customer {
      String name
    }
  • CustomerDao.groovy:
    package org.groovy.cookbook
    class CustomerDao {
      Customer getCustomerById(Long id) {
        // DO SOME DATABASE RELATED QUERY
        ...
      }
    }
  • CustomerService.groovy:
    package org.groovy.cookbook
    class CustomerService {
      CustomerDao dao
      void setCustomerDao(CustomerDao dao) {
        this.dao = dao
      }
      Customer getCustomer(Long id) {
        dao.getCustomerById(id)
      }
    }

The fictional CustomerService class has a dependency towards CustomerDao that is somehow injected at runtime (dependency injection is a well-known pattern made famous by the ubiquitous Spring framework). The CustomerDao is a class that would normally access some kind of data store to retrieve the customer data. We are not interested in the details of how the DAO accesses the database. What we know is that unit testing the CustomerService class would be very difficult without satisfying the hard dependency of the DAO and the database. In other words, the CustomerService class can be only tested if the database required by the DAO is actually running.

In the context of this recipe, the DAO's method getCustomerById does nothing; but in real life, it would contain code to query the database.

How to do it...

A unit test is all we need to put this simple mocking technique into practice:

  1. Let's write a unit test for CustomerService, as follows:
    package org.groovy.cookbook
    
    import static org.junit.Assert.*
    import org.junit.*
    class TestCustomerService  {
      @Test
      void testGetCustomer() {
        boolean daoCalled = false
        CustomerDao.metaClass.getCustomerById = { Long id ->
          daoCalled = true
          new Customer(name:'Yoda')
        }
        def cs = new CustomerService()
        def cDao = new CustomerDao()
        cs.setCustomerDao(cDao)
        def customer = cs.getCustomer(100L)
        assertTrue(daoCalled)
        assertEquals(customer.name, 'Yoda')
      }
    }

How it works...

The unit test has the usual plumbing code to instantiate the class under test (CustomerService), and injects the CustomerDao into the service. Before that part, we override the getCustomerById method by overwriting the method with our own closure. The intercept and replace mechanism is again based on the ExpandoMetaClass class and the metaprogramming features of Groovy. When the mocked method is invoked, the closure is triggered, and the local daoCalled variable gets updated.

A small detail that you should remember is you need to qualify the first argument with the type if the method has a typed parameter; otherwise Groovy won't override the behavior. These two examples would not work:

CustomerDao.metaClass.getCustomerById = { ->
  ...
}

CustomerDao.metaClass.getCustomerById = { id ->
  ...
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.37.151