1 Jan 2010

Using GMock to complement Grails mockDomain

Since Grails 1.1 we've had pretty good unit testing support via GrailsUnitTestCase and its sub-classes. The mockDomain method is particularly useful for simulating the various enhancements Grails adds to domain classes. However, there are some domain class capabilities, such as criteria queries and the new named queries, that can't really be simulated by mockDomain.

So assuming we're trying to unit test a controller that uses criteria methods or named queries on a domain class how can we enhance the capabilities of mockDomain? One of my favourite Groovy libraries is GMock which I use in preference to Groovy's built in mock capabilities. One of its really powerful features is the ability to use 'partial mocks', i.e. to mock particular methods on a class whilst allowing the rest of the class to continue functioning as normal. This means we can layer a mocked createCriteria, withCriteria or named query call on to a domain class that is already enhanced by mockDomain.

First off you need to add the GMock dependency to your BuildConfig.groovy. Since GMock supports Hamcrest matchers for matching method arguments you'll probably want those as well:
    dependencies {
        test "org.gmock:gmock:0.8.0"
        test "org.hamcrest:hamcrest-all:1.0"
    }
If you're using an earlier version of Grails you'll need to just grab the jar files and put them in your app's lib directory.

Then in your test case you need to import GMock and Hamcrest classes and add an annotation to allow GMock to work:
    import grails.test.*
    import org.gmock.*
    import static org.hamcrest.Matchers.*

    @WithGMock
    class MyControllerTests extends ControllerUnitTestCase {

Adding criteria and named query methods is now fairly simple:

Mocking a withCriteria method

    def results = // whatever you want your criteria query to return
    mock(MyDomain).static.withCriteria(instanceOf(Closure)).returns(results)
    play {
        controller.myAction()
    }
Breaking this example down a little
  1. mock(MyDomain) establishes a partial mock of the domain class.
  2. instanceOf(Closure) uses a Hamcrest instanceOf matcher to assert that the withCriteria method is called with a single Closure argument (the bit with all the criteria in).
  3. returns(results) tells the mock to return the specified results which here would be a list of domain object instances.
In this example we're expecting the withCriteria method to be called just once but GMock supports more complex time matching expressions if the method may be called again.

Mocking a createCriteria method

The withCriteria method returns results directly but createCriteria is a little more complicated in that it returns a criteria object that has methods such as list, count and get. To simulate this we'll need to have the mocked createCriteria method return a mocked criteria object.
    def results = // whatever you want your criteria query to return
    def mockCriteria = mock() {
        list(instanceOf(Closure)).returns(results)
    }
    mock(MyDomain).static.createCriteria().returns(mockCriteria)
    play {
        controller.myAction()
    }
This is only a little more complex than the previous example in that it has the mocked list method on another mock object that is returned by the domain class' createCriteria method. mock() provides an un-typed mock object as we really don't care about the type here.

Mocking a named query

For our purposes named queries are actually pretty similar to createCriteria.
    def results = // whatever you want your criteria query to return
    def mockCriteria = mock() {
        list(instanceOf(Closure)).returns(results)
    }
    mock(MyDomain).static.myNamedQuery().returns(mockCriteria)
    play {
        controller.myAction()
    }

Some other examples:

Mocking a named query with an argument

    mock(MyDomain).static.myNamedQuery("blah").returns(mockCriteria)
For simple parameters you don't need to use a Hamcrest matcher - a literal is just fine.

Mocking a withCriteria call using options

You can pass an argument map to withCriteria, e.g. withCriteria(uniqueResult: true) { /* criteria */ } will return a single instance rather than a List. To mock this you will need to expect the Map as well as the Closure:
    def result = // a single domain object instance
    mock(MyDomain).static.withCriteria(uniqueResult: true, instanceOf(Closure)).returns(result)

Mocking criteria that are re-used

It's fairly common in pagination scenarios to call a list and count method on a criteria object. We can just set multiple expectations on the mock criteria object, e.g.
    def mockCriteria = mock() {
        list(max: 10).returns(results)
        count().returns(999)
    }
    mock(MyDomain).static.myNamedQuery().returns(mockCriteria)

The nice thing about this technique is that it doesn't interfere with any of the enhancements mockDomain makes to the domain class, so the save, validate, etc. methods will still work as will dynamic finders.

Be aware however, that what we're doing here is mocking out the criteria queries, not testing them! All the interesting stuff inside the criteria closure is being ignored by the mocks and could, of course, be garbage. Named queries are pretty easy to test by having integration test cases for your domain class. Criteria queries beyond a trivial level of complexity should really be encapsulated in service methods or named queries and integration tested there. Of course, GMock then makes an excellent solution for mocking that service method in your controller unit test.

80 comments:

  1. Good to see a blog introducing GMock. :)

    Actually, "mock(MyDomain).static..." is called static mocking, and partial mocking is something like "mock(controller)...". Nevertheless, static mocking is also mocking static methods partially, so you can call it "static partial mocking". :)

    There is a new feature called chained methods in the incoming GMock 0.9.0 can simplify the mocking the createCriteria(). You can chains the methods like "mock(MyDomain).static.createCriteria().chains().list(...).returns(results)".

    What's more, you can also test the closure of withCriteria() in GMock 0.9.0 like:
    mock(MyDomain).static.withCriteria(invoke {
    between(...)
    like(...)
    }).returns(results)

    ReplyDelete
  2. Nice, I didn't know about that feature.

    Chaining will be really useful as well.

    ReplyDelete
  3. Awesome post Rob, just what I needed!

    ReplyDelete
  4. Rob, I'm trying to follow your examples, mainly with the named queries, but in my case to test the named query itself.

    So I have something like (sorry for my spanish coding):

    def results = [] << Historico.findByProgramaID('2') << Historico.findByProgramaID('3') << Historico.findByProgramaID('4')

    def mockCriteria = mock() {
    list(instanceOf(Closure)).returns(results)
    }

    mock(Historico).static.todosActivos().returns(mockCriteria)

    And then when I try to play it, I'm getting an error:

    play {
    assert results == Historico.todosActivos()
    }

    Could it be even possible to do something like this?:

    play {
    def r = Historico.todosActivos()
    assert ['2', '3', '4'] == r*.programaID

    Thanks in advance.

    ReplyDelete
  5. I think it should be

    assert results == Historico.todosActivos().list {...}

    ReplyDelete
  6. Unfortunately I get this error when trying the "Historico.todosActivos().list { ... }:

    nexpected method call 'list()' on 'Mock for Object' 'list(an instance of groovy.lang.Closure)' on 'Mock for Object': expected 1, actual 0 'Historico.todosActivos()': expected 1, actual 1

    junit.framework.AssertionFailedError: Unexpected method call 'list()' on 'Mock for Object'
    'list(an instance of groovy.lang.Closure)' on 'Mock for Object': expected 1, actual 0
    'Historico.todosActivos()': expected 1, actual 1
    at org.gmock.internal.InternalMockController.fail(InternalMockController.groovy:185)
    at org.gmock.internal.InternalMockController$fail.call(Unknown Source)
    at org.gmock.internal.metaclass.MetaClassHelper.findExpectation(MetaClassHelper.groovy:25)
    at org.gmock.internal.metaclass.MetaClassHelper$findExpectation.callStatic(Unknown Source)
    at org.gmock.internal.MockInternal.invokeMockMethod(MockInternal.groovy:80)
    at org.gmock.internal.metaclass.MockProxyMetaClass$2.call(MockProxyMetaClass.java:53)
    at org.gmock.internal.InternalMockController.doWork(InternalMockController.groovy:218)
    at org.gmock.internal.InternalMockController.this$2$doWork(InternalMockController.groovy)
    at org.gmock.internal.InternalMockController$this$2$doWork.callCurrent(Unknown Source)
    at org.gmock.internal.InternalMockController$this$2$doWork.callCurrent(Unknown Source)
    at org.gmock.internal.InternalMockController.doInternal(InternalMockController.groovy:207)
    at org.gmock.internal.InternalMockController$doInternal.callCurrent(Unknown Source)
    at org.gmock.internal.InternalMockController$doInternal.callCurrent(Unknown Source)
    at org.gmock.internal.InternalMockController.doInternal(InternalMockController.groovy:200)
    at org.gmock.internal.metaclass.MockProxyMetaClass.invokeMethod(MockProxyMetaClass.java:47)
    at org.gmock.internal.metaclass.MockProxyMetaClass.invokeMethod(MockProxyMetaClass.java:43)
    at es.cuestamenos.dominio.HistoricoTests$_testTodosActivos_closure2.doCall(HistoricoTests.groovy:83)
    at es.cuestamenos.dominio.HistoricoTests$_testTodosActivos_closure2.doCall(HistoricoTests.groovy)
    at org.gmock.internal.InternalMockController.play(InternalMockController.groovy:116)
    at org.gmock.internal.InternalMockController$play.call(Unknown Source)
    at org.gmock.GMockController.play(GMockController.groovy:29)
    at org.gmock.GMockController$play.call(Unknown Source)
    at es.cuestamenos.dominio.HistoricoTests.play(HistoricoTests.groovy)
    at es.cuestamenos.dominio.HistoricoTests.testTodosActivos(HistoricoTests.groovy:82)

    Anything else I could try?

    ReplyDelete
  7. I think you should use "list{}" instead of "list()"

    ReplyDelete
  8. That did the trick. I forgot that I defined the mock as list(instanceOf(Closure)) ;-)

    Thanks!

    ReplyDelete
  9. Actually, I just realized that this trick is great to simulate the named query, but not to test it, as it will always pass the test, right?

    What are your thoughts on this?

    ReplyDelete
  10. Yes, this technique is *not* suitable for testing the query. It's for testing the logic in code that uses the query without having to connect to a real database or set up real test data.

    All you're doing in that code is testing the mock you just set up.

    ReplyDelete
  11. Rob, and any idea on how I could test the named query itself meanwhile support for mockDomain is added to Grails?

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Thanks for the great example. It was a big help to us starting out with Grails.
    If your named query takes parameters, you need to define it a bit differently.

    def results=[]
    def params
    def mockCriteria = mock() {
    list().returns( results)
    }
    mock(Keyword).static.myNamedQuery(params)
    .returns(mockCriteria)

    ReplyDelete
  14. Just a remark: mocking static methods using GMock will make tests fall if you or some Grails plugin need to call other static methods on particular class. The fix is to combine GMock with the standard Groovy metaprogramming:

    def results = // whatever you want your criteria query to return
    def mockCriteria = mock() {
    list(instanceOf(Closure)).returns(results)
    }
    MyDomain.static.createCriteria = { -> mockCriteria}

    play {
    controller.myAction()
    }

    ReplyDelete
  15. Thanks for your sharing! The information your share is very useful to me and many people are looking for them just like me! thank you! I hope you have many useful articles to share with everyone!
    slither io

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. This comment has been removed by the author.

    ReplyDelete
  18. Essentially, an abstract on research paper is an extrapolation of course works written in a report format specified by the professor. Typically, an abstract consist of one paragraph that summarizes the contents of the article in precise terms.

    ReplyDelete
  19. Thanks for providing great information and looking beautiful blog, extremely decent required data and the things I never envisioned and I would ask for, wright more blog and blog entry like that for us Top Gun Jacket

    ReplyDelete
  20. This is the first time that I visit here. I found so many exciting matters in this particular blog, One thing I would like to request you that pls keep posting such type of informative blog. Movie Jackets

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete
  22. This blog is very informative thanks for sharing this with us. I appreciate your work. Check out our 100% leather
    Top Gun Jacket and also check Film jacket, Gaming jacket, TV series jacket and many more.

    ReplyDelete
  23. Your blog is awesome I got all those things what I want to know. Thanks for sharing. Check out our new famous outfit The Gentlemen Coach Tracksuit

    ReplyDelete
  24. Top quality leather jackets available for our clients from the world of sports featured in the WWE Mens Jackets

    ReplyDelete
  25. I'm glad by reading your fascinating article, also I love this The Genuine Leather, Keep uploading more interesting articles.

    ReplyDelete
  26. Bravo!! what an interesting post. Very interesting information. Visit -> Bearingpk

    ReplyDelete
  27. This is the first time that I visit here. I found so many exciting matters in this particular blog, One thing I would like to request you that pls keep posting such type of informative blog. Assassin’s Creed Coat

    ReplyDelete
  28. This is such an amazing resources, that you were giving me the how you looking the part of it. I'm happy to have discovered this post as its such an attractive one! Altered Carbon Coat

    ReplyDelete
  29. I am consistently watchful for quality posts and articles so I think I'm successful to have discovered this! Joaquin Phoenix Coat

    ReplyDelete
  30. This website is about leather jackets.
    https://superstarjackets.com/product/get-smart-agent-99-anne-hathaway-white-leather-jacket/

    ReplyDelete
  31. Amazing leather jacket for winters.
    https://superstarjackets.com/product/gigi-hadid-black-double-rider-genuine-leather-jacket-available-online/

    ReplyDelete
  32. I like this jacket. Thank you so much
    https://superstarjackets.com/product/gigi-hadid-black-shearling-leather-jacket-available-online/

    ReplyDelete
  33. Come to our site. www.superstarjackets.com
    https://superstarjackets.com/product/gigi-hadid-brown-cropped-leather-jacket/

    ReplyDelete
  34. Wanna buy leather jackets with extreme Quality and same as want ???
    https://superstarjackets.com/product/irina-shayk-slim-fit-double-breasted-brown-leather-trench-coat/

    ReplyDelete
  35. We are here to provide you upper class quality in leather Jackets. You can buy any type of celebrity jackets from www.superstarjackets.com
    https://superstarjackets.com/product/isla-fisher-leather-jacket/

    ReplyDelete
  36. Superstar Jackets have each celebrity jackets you ever seen on TV shows, movies, and seasons. You can buy from www.superstarjackets.com
    https://superstarjackets.com/product/jenna-coleman-doctor-who-clara-oswald-black-biker-leather-jacket/

    ReplyDelete
  37. The BWC is also at an advantageous location because its juxtaposition to the proposed Rawalpindi Ring Road is the nearest compared to all other upcoming housing societies being developed in the area. The 4 unique accesses to BWC is a real estate investor’s hotspot in Islamabad. The future holds the immense potential of fantastic returns on investments in this housing society because of its central location and state-of-the-art infrastructure.
    Blue world city Islamabad installment plan
    park view lahore payment plan
    Rudn Enclave payment plan

    ReplyDelete

  38. Such a very useful article. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. THE LAST OF US PART II JACKETS

    ReplyDelete
  39. Wow such an amazing content I really like it. Here is discounted coupon fr you avail it now from here wanikani promo code

    ReplyDelete
  40. This is an amazing post for all the viewer's thanks for creating it journalist killed in afghanistan.

    ReplyDelete
  41. Hy I'm Designer For Customize Leather Jackets. Please Visit Our Website. Teen Wolf Derek Hale Faux Leather Jacket

    ReplyDelete
  42. Thanks for sharing this amazing blog with some unique information keep it up. Here is my new bloxland promo code

    ReplyDelete
  43. This comment has been removed by the author.

    ReplyDelete
  44. Pakistan insider Hey! Thanks for the info. This info helped me a lot. I will wait some more.

    ReplyDelete
  45. HARDCORE CYCLES was started in the Greater Philadelphia area by a group of tight friends with a passion for V-Twin performance aftermarket parts.Visit our Website Thanks
    Feuling LIFTERS 508 RAC CH DRV M8

    ReplyDelete
  46. Your article is really addictive. Keep posting. keep sharing the knowledge. I love to read your articles. Thank you for sharing this article with us. This article will make a good reference for me. Thanks a lot. It is appreciated.
    Best real estate company in Islamabad

    ReplyDelete
  47. Nice article! It's a great experience for this website. It's really helpful for me. So I have also recommended you come to this site. Take My Online Course

    ReplyDelete
  48. We all face strict deadlines to complete do exam for me Do exams for me provide the best help for your queries like “can I hire someone to do my exam?” to complete your paper just in time our experts are available all day and night.

    ReplyDelete
  49. Anzo Marketing is one of the top real estate marketing companies that deal with top housing projects like NOVA City Islamabad. Customer satisfaction is our top priority.

    ReplyDelete
  50. Cosmetics and beauty products can help our skin in a number of ways. However, because everyone has various skin types, it is up to each person to determine CBD Skin Care Wholesale which product will be their greatest helper. For this reason, we created our cosmetic line, which consists of 5 original formulas and provides a wide range of skin types with several uses, including:

    ReplyDelete
  51. This is quite interesting. I am so glad that you have shared this amazing blog post. I will definitely share this informative blog with my friends and other social sites. Mens Suits

    ReplyDelete
  52. Amazing. Previously I had no idea about this feature.

    XNO Sports

    ReplyDelete
  53. It's the right time to change your SEO services agency if your site has failed to generate traffic even after months. Social Ninja is the one online stop shop where you can find the best solutions for your website’s SEO ranking. We pay attention to web development, content marketing, digital marketing, e-commerce, and other services to boost the site's traffic by making it more attractive. We work to retain the clients and not just work with them once. Our company strives to give you a great experience every time with mind-blowing SEO services. Reach us now so that we start a discussion today. 
    http://socialninjaagency.com/

    ReplyDelete
  54. Fashion is the thing that you make it – The primary thing you need to comprehend about fashion is that it is just what you make it, that’s it. Princess Diana Jacket

    ReplyDelete
  55. quironsalud
    https://quironsalud.ae
    The finest staff, the most advanced technology, research, training, and a common management model all back the Group's commitment to quality services for all citizens. Quirónsalud covers all medical specialties, providing particularly outstanding service in diagnosis and treatment of cardiovascular disease and cancer.

    ReplyDelete
  56. https://montessorivision.com/products/magical-tracing-workbook
    Do you want your children to develop their creativity and imagination while developing problem-solving skills and coordination? Montessori vision can help you with that. Our range includes classic wooden memory blocks, magic tracing workbook, magical drawing book, puzzles, Montessori animals, worm game, Montessori train set, Montessori toys target, and modern digital and basic science kits. Whether they choose to explore with our blocks, instruments, books, art, and activity supplements or spend many hours interacting with our apps and video entertainment built right into some of our most advanced intelligent mirrors yet, your little ones will enjoy an unrivaled experience in proprioception, expressive language development, cognitive enrichment, and physical externality. In short, we teach your children all the different ways to play and learn

    ReplyDelete
  57. People frequently decline to move to various nations due to the hustle of visa application processes, payment, documentation, and settlement in another country. But, DM Consultants give adequate movement administrations to individuals of India to effortlessly go to another state and start a new life by depending on us with all legal procedures. If you are confused, get in touch with us through our site or visit our office in India for good consultancy and direction
    https://www.dm-consultant.in/

    ReplyDelete
  58. Such an interesting article here.I was searching for something like that for quite a long time and at last I have found it here. wyatt langmore Jacket

    ReplyDelete
  59. Why Dessert Safari is So Famous

    Finding the right kind of blogs for your everyday read could be a hustle, but with Technology Center UAE it is a work of just minutes. We offer you a wide variety of blogs and articles that full fill your likings. With us you will find pieces that will add up a lot to your knowledge. Skim through our website today and find what you like.

    https://uaetechcenter.com/

    ReplyDelete
  60. Mrs kapri Loup

    Travel Hub
    This blog provides a quick guide on everything you need to know before renting a car in Dubai. It covers important topics such as driving requirements, rental fees, insurance, and more. Whether you're a tourist or a resident, this guide can help you make informed decisions when renting a car in Dubai.
    https://traveluaehub.com/

    ReplyDelete
  61. Looking for something incredible to read in your daily routine? We heard you! local company 24 is thrilled to bring to you the best kinds of read that can make your everyday knowledge better and wider. With us you will get to read blogs and articles that are easy to read and understand. Visit out website today and find your choice of articles and blogs today.
    https://localcompany24.com/

    ReplyDelete
  62. "This blog has a unique ability to spark curiosity and encourage further exploration. The writer introduces fascinating topics and leaves readers with a desire to dive deeper into the subject matter. It's a gateway to endless learning."
    Costco Tire Promo Code.

    ReplyDelete
  63. See change as an invitation to let go of attachments and embrace flow.top gun leather jacket

    ReplyDelete
  64. Uncovers the hidden symmetries that link theories from different realms.Kill Em With Comedy Hoodie

    ReplyDelete
  65. The tapestry of your writing is woven with intricate metaphors and analogies. Tom Cruise Top Gun Jacket

    ReplyDelete
  66. Possesses a rare capacity for extracting essential insights from convoluted theoretical constructs.<a href="https://cosplaystreet.com/collections/best-seller/products/top-gun-jacket>top gun maverick jacket original</a>

    ReplyDelete
  67. Your love for Stranger Things just got a cozy upgrade. Introducing the Stranger Things Puffer Jacket – designed for fans who know that adventure and style go hand in hand. Secure yours before they vanish like demogorgons.

    ReplyDelete
  68. Exploring GMock alongside Grails' mockDomain is like finding a solution as satisfying as learning "how to cancel regal unlimited." Your blog provides valuable insights on this combination, enhancing our understanding.

    ReplyDelete

  69. Implementing GMock alongside Grails' mockDomain is a strategic move that adds a powerful layer to your testing strategy. franklin graham home value The combination allows for a more comprehensive and flexible testing environment in Grails applications.

    ReplyDelete
  70. Saima Group offers the Best investment plans 2024. Their completed projects in various places in Karachi demonstrate their dedication to delivering affordable homes in Karachi. Saima Group provides simple payment plans, making it easier for people to obtain their dream houses.

    ReplyDelete
  71. Beth Dutton pink handbag is a must-have for any fashion lover. Its vibrant color and classic design create a perfect blend of style and elegance.
    Site: Exclusive beth dutton handbag

    ReplyDelete
  72. Elevate your style with the Ted Lasso jacket collection. These jackets are a blend of comfort, style, and fan-favorite designs.
    buy ted lasso series

    ReplyDelete
  73. As technology continues to evolve, its impact on education can’t be denied or overlooked. From online classes to interactive learning platforms, technology is remodeling how students take up their academic challenges now. All this has given rise to my assignment help services online—and rightly so. Here, you’ll know how these services enhance the educational experience and contribute to student success today.

    https://essaywritinghelp.co.uk/

    ReplyDelete