Fork me on GitHub

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.

77 comments:

Johnny Jian said...

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)

Rob said...

Nice, I didn't know about that feature.

Chaining will be really useful as well.

Steve Dalton said...

Awesome post Rob, just what I needed!

Quique said...

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.

Johnny Jian said...

I think it should be

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

Quique said...

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?

Johnny Jian said...

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

Quique said...

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

Thanks!

Quique said...

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?

Rob said...

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.

Quique said...

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

Ursula said...
This comment has been removed by the author.
Ursula said...

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)

Unknown said...

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()
}

agario games said...

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

Unknown said...
This comment has been removed by the author.
Unknown said...
This comment has been removed by the author.
Unknown said...

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.

Fashion said...

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

Unknown said...

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

AmericanExpress said...

https://americanexpresscomconfirmcard.live/ add comments

Priya Escorts said...
This comment has been removed by the author.
John Lilly said...

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.

Emma Jackson said...

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

shopify said...

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

John Lilly said...

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

Ali Khan said...

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

Unknown said...

best post
Playerunknown’s Battlegrounds Leather Jacket

riscillalauryn91@gmail.com said...

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

riscillalauryn91@gmail.com said...

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

riscillalauryn91@gmail.com said...

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

Famous Jacket said...

Buy this Wool Blend Wes Bentley Grey Jacket SHOP NOW......


Famous Jacket

game areena said...

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

game areena said...

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

game areena said...

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

game areena said...

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

game areena said...

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/

game areena said...

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/

game areena said...

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/

Deal and Delas said...

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

LerenJack said...


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

Barbara Lee said...

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

Anonymous said...

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

EnglishJackets said...

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

Merry Smith said...

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

Dollar Jackets said...
This comment has been removed by the author.
Anonymous said...

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

Right Jackets said...

RightJackets is the best place for you to get the most original custom made products and more.We have the biggest range of a variety of products.

Calf Skin Brown Leather Jacket
HUF Reversible Silver Easy Satin Jacket
Casual Purple Motorcycle Leather Jacket
God Created Adam & Eve Not Adam & Steve Jacket
Ladies Brando Green Fitted Suede Leather Jacket

rockstarjackets said...

RockStar Jackets is incredibly pleased to announce ourselves as the most dependable manufacturers and exporters of a variety of products comprising of Leather Garments.

Burberry Brit Shearling-trimmed Moto Leather Jacket
Singer Justin Bieber ‘Believe Tour’ at The Valley View Casino Center Leather Jacket
Mackage Fur-trimmed Glen Down Bomber Leather Jacket
Haute Hippie Brown Biker Leather Jacket
Reformation Style Black Leather Jacket

Unknown said...

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

kathymccaan said...

Shop this best quality Kayce Dutton Yellowstone Brown Jacket For men SHOP NOW

ahrealestatemarketing said...

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

Smith said...

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

Do My Online Exam Now said...

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.

aleenamishal said...

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.

paulwin said...

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:

Anna said...

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

thealey said...

l6e17s0f10 n3e32b3a93 e5s31i1r06 z9s32j7f23 i5k90n4i55 z1w98g3f24

XNO Sports said...

Amazing. Previously I had no idea about this feature.

XNO Sports

socialninjaagency said...

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/

keniazugei said...

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

Quiron Salud Hospital said...

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.

Montessori Vision said...

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

DM Consultants Pune said...

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/

Stephen said...

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

Technology Center UAE said...

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/

Tires & More - Tyre Shop Dubai said...

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/

Local Company 24 said...

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/

Edvin Berg said...

"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.

koni said...

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

koni said...

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

Finn Foley said...

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

koni said...

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>

Emma watson said...

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.

Jennifer Walsh said...

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.

Lilly Anna said...


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.

albaflores said...

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.