Victor is a ruby developer at Nulogy. He has worked a lot with Java and Ruby platforms. Being a big fan of domain specific languages he likes to blog about implementing them using Groovy, Ruby or Clojure. Victor is a DZone MVB and is not an employee of DZone and has posted 44 posts at DZone. You can read more from them at their website. View Full User Profile

Mocking a Hessian Web Service Using Groovy

02.25.2011
| 6843 views |
  • submit to reddit

During last month there were a lot of moments when I thought: ‘How cool it is that I can use Groovy to write it’. For instance, I was working on mocking a hessian web service a few days ago. Hessian is binary web service protocol and writing a mock web service might have been a tricky task. At least I though so in the beginning. But it turned out that everything can be done in a clear and neat way if you have right tools. To show how I used Groovy to do it let’s take a very simple interface that has a hessian based implementation:

interface SampleHessianService {
    String sayHi(String m)
}

Our goal is to write an utility class mocking this service so we can write integration tests for our client (for example, a web app). The client of the service uses such a chunk of code to access the service:

def factory = new HessianProxyFactory()
def client = factory.create(SampleHessianService, "http://localhost:8080/sample")
String response = client.sayHi('John')

To write my mock service in old Java world I would write something like this:

public class MyServlet extends HessianServlet implements SampleHessianService {
    Object mockObject

    public String sayHi(String m){
        ....
        //somehow delegate this call to mockObject 
        ....
    }
}

MyServlet servlet = new MyServlet();
//creating a jetty server, creating a jetty context
context.addServlet(new ServletHolder(servlet), "/sample");
//starting the jetty server

There are a few things that bother me in this approach. If the interface had like 20 methods I would have to implement all of them to delegate all the work to my mockObject. But I don’t work to write this kind of stuff every time I need a Hessian service. Ideally, I would like to pass a Spock mock object (or any other mock object) to generate a Hessian servlet.

The first feature of Groovy I tried to use to improve my old java style version is @Delegate. @Delegate annotation implements all the methods of SampleHessianService for me and delegates all the calls to a specified object:

    class SampleHessianService_HessianImpl extends HessianServlet {
        @Delegate SampleHessianService delegate
    }
    def hessian = new SampleHessianService_HessianImpl()
    hessian.delegate = Mock(SampleHessianService)

It looks much better, doesn’t it? But I still need to write this dummy class. Writing boilerplate code is OK in Java but not in Groovy. So the next step I took was generating SampleHessianService_HessianImpl in runtime:

    def type = SampleHessianService
    def fullName = type.name
    def className = type.simpleName

    def classText = """
        import com.caucho.hessian.server.HessianServlet
        class ${className}_HessianImpl extends HessianServlet {
            @Delegate ${fullName} delegate
        }
    """
    def gcl = new GroovyClassLoader()
    def clazz = gcl.parseClass(classText)
    def res = clazz.newInstance()

There is one thing I don’t like in generators: I have to add an additional step to my build process to do it. But not in Groovy. Using GroovyClassLoader you can parse new classes easily in runtime without any additional steps. Cool.

The result of my work is this very small but useful class:

class HessianServletFactory {

    HessianServlet createHessianServlet(Map args) {
        assert args.type instanceof Class, 'type parameter is not passed or is not a class'
        assert args.delegate, 'delegate parameter is not passed'

        def classText = genClassText(args.type)

        def gcl = new GroovyClassLoader()
        def clazz = gcl.parseClass(classText)
        def res = clazz.newInstance()
        res.delegate = args.delegate
        return res
    }

    private genClassText(type){
        def fullName = type.name
        def className = type.simpleName

        """
            import com.caucho.hessian.server.HessianServlet
            class ${className}_HessianImpl extends HessianServlet {
                @Delegate ${fullName} delegate
            }
        """
    }
}

The way you can use it in your test:

@Mixin(HessianServletFactory)
class HessianServletSpec extends Specification {

    def 'should create a hessian servlet using a type and a spock mock object'() {
        setup: 'setting up server part'
        def service = createHessianServlet(type: SampleHessianService, delegate: Mock(SampleHessianService))
        //adding it to jetty, starting jetty

        and: 'setting up client part'
        def factory = new HessianProxyFactory()
        def client = factory.create(SampleHessianService, "http://localhost:8080/sample")

        when:
        def response = client.sayHi(NAME)

        then:
        service.delegate.sayHi(NAME) >> RESPONSE
        response == RESPONSE

        where:
        NAME = 'John'
        RESPONSE = 'Hi John'
    }
}

As you can see mocking a hessian servlet using HessianServletFactory is easy. You don’t have to create new classes each time you want to do it, you don’t have to add an additional step to your build process. The only thing you have to do is to specify a type and a delegate. It can be a Spock mock object or just a fake implementation. Also, I’ve used Mixin in my test because the factory doesn’t have any state and I have not wanted to create a dummy receiver: new HessianServletFactory().createHessianServlet(...) I could use a static method here too. But I prefer never use static methods unless it is a one line method that just creates an object.

 

From http://victorsavkin.com/post/3473984162/mocking-a-hessian-web-service-using-groovy

Published at DZone with permission of Victor Savkin, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: