Grails 2 Testing: Mock once, always mocked

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Grails 2 Testing: Mock once, always mocked

danielsweb
Upgrading from Grails 1.3.7 to Grails 2.2.2, I have a problem. Once I mock a non-Grails class using groovy.mock.interceptor.MockFor it is mocked for all other tests from that point on. For example, if I mock a class in test 50 of 200, then for tests 51-200 it will remain mocked. This is problematic for me. I want finer control over when a class is mocked. What I expect to happen is that in test 50 the class is mocked, but in test 51 the class is no longer mocked - unless I "re-mock" it.

Please help.

Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

danielsweb
This post was updated on .
Here's a simple way to duplicate the problem:

Controller:
package test

class BookController {

    def index() {
      def o = new MyObject()
      render o.name
    }

}

class MyObject {
  def name = 'Daniel'
}

Test:
package test

import grails.test.mixin.*
import org.junit.*
import groovy.mock.interceptor.MockFor

/**
 * See the API for {@link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
 */
@TestFor(BookController)
class BookControllerTests {

    def myObjectMetaClass

    @Before
    public void before() {
     
    }

    @After
    public void after() {

    }

    void testIndexWithMock() {
      def mockObject = new MockFor(MyObject)
      mockObject.demand.getName(1) {
        return "George"
      }

      mockObject.use {
        controller.index()
      }

      assertEquals 'George', response.text
    }

    void testIndexWithoutMock() {
      controller.index()
      assertEquals 'Daniel', response.text
    }

}

If the unit tests run in the following order: testIndexWithMock, testIndexWithoutMock - I'll get the following exception:

Failure:  testIndexWithoutMock(test.BookControllerTests)
|  junit.framework.AssertionFailedError: No more calls to 'getName' expected at this point. End of demands.
        at test.BookController.index(BookController.groovy:7)

If they run in the other order: testIndexWithoutMock, testIndexWithMock - then the tests run just fine - but if I had more tests to run after this, the MyObject class is essentially tainted and cannot be used. It's like the MyObject.metaClass is messed with and not restored.

I'm looking at the issue further. Could use any help the community can provide.
Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

danielsweb
This post was updated on .
In reply to this post by danielsweb
To be clear, I'm aware of Grail's mockFor() function. The problem is it doesn't work.

If I were to change my testIndexWithMock() implementation as follows:

  void testIndexWithMock() {
    def mockObject = mockFor(MyObject)
    mockObject.demand.getName(1) {
      return "George"
    }

    controller.index()

    assertEquals 'George', response.text
  }

I get the following error:

| Failure:  testIndexWithMock(test.BookControllerTests)
|  junit.framework.ComparisonFailure: expected:<[George]> but was:<[Daniel]>
  at junit.framework.Assert.assertEquals(Assert.java:85)
  at test.BookControllerTests.testIndexWithMock(BookControllerTests.groovy:46)

This means my mock was never used in the implementation. I think this is because the class I'm mocking - MyObject - is not a Grails type class (e.g., Domain, Service, Controller, etc.). But perhaps I'm using it wrong. I've read the documentation on this, but it's not clear how the orchestration happens.
Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

Denny Wordtwo
Yes, your Mock was never used in the implementation. That is why it fails.

You declare a new MyObject in the controller. But this is surely not the Mock you declared in the unit test. But you could do the following (if it's an option for you):

    void testIndexWithMetaClass() {
        MyObject.class.getName = {
            return 'George'
        }

        controller.index()

        assertEquals 'George', response.text
    }



As for your first post I can't tell you the answer. But it is known that the Unit test clean up after mocking does not work quite well in Grails. For example cleaning used Mixins after Unit tests does not work, too.
Do you searched Grails Jira for this issue?
Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

danielsweb
Sadly that didn't work. But it was a nice idea. I'll paste the data below, but note that I've changed things a bit since my first example.

When I run the test, I get the following exception:

| Running 2 unit tests... 1 of 2
| Failure:  testIndexWithGrailsMock(test.BookControllerTests)
|  java.lang.ClassFormatError: Illegal class name "test/BookControllerTests$_testIndexWithGrailsMock_closure2@49669be$name" in class file test/BookControllerTests$_testIndexWithGrailsMock_closure2@49669be$name
        at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
        at test.BookController.index(BookController.groovy:7)
        at test.BookControllerTests.testIndexWithGrailsMock(BookControllerTests.groovy:69)
| Running 2 unit tests... 2 of 2
| Failure:  testIndexWithoutMock(test.BookControllerTests)
|  java.lang.ClassFormatError: Illegal class name "test/BookControllerTests$_testIndexWithGrailsMock_closure2@49669be$name$0" in class file test/BookControllerTests$_testIndexWithGrailsMock_closure2@49669be$name$0
        at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
        at test.BookController.index(BookController.groovy:7)
        at test.BookControllerTests.testIndexWithoutMock(BookControllerTests.groovy:75)
| Packaging Grails application.....
| Packaging Grails application.....
| Tests FAILED  - view reports in /opt/workspace-other/test/target/test-reports


Here's the code, in case I messed something up:

Test Class:
package test

import grails.test.mixin.*
import org.junit.*

@TestFor(BookController)
class BookControllerTests {

  void testIndexWithGrailsMock() {
    def mock = mockFor(MyObject, true)
    mock.demand.static.doSomething {-> return 'Roger'}

    MyObject.class.name = {-> return 'George'}

    def model = controller.index()
    assert 'George' == model.name
    assert 'Roger'  == model.something
  }

  void testIndexWithoutMock() {
    def model = controller.index()
    assert 'Daniel' == model.name
    assert 'Joe'    == model.something
  }

}


Controller:
package test

class BookController {

  def index() {
    def o = new MyObject()
    [name: o.name(), something: MyObject.doSomething()]
  }

}

class MyObject {
  static def doSomething() { return 'Joe' }
  def name() { return 'Daniel' }
}

Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

danielsweb
This post was updated on .
In reply to this post by danielsweb
A bug has been filed:

http://jira.grails.org/browse/GRAILS-8535

What concerns me is that it keeps getting bumped.

I'm recommending we DON'T upgrade to Grails 2 until this gets fixed. And if it doesn't get fixed soon, I'm recommending we re-write our application using Ruby on Rails (over JRuby). Because that would be faster and better than retrofitting our application to fit the testing framework.
Reply | Threaded
Open this post in threaded view
|

Re: Grails 2 Testing: Mock once, always mocked

longwa
I submitted a pull request for the 2.2.x branch that seems to fix this issue for us. We have a simple case where we use the groovy StubFor in a unit test and then later on it fails b/c the mock is still being used in another test. The test work fine if you run them in the reverse order which is very frustrating.


Is there anyway you can test with my patch to see if it fixes your issue?

-Aaron


On Thu, May 9, 2013 at 9:54 AM, danielsweb <[hidden email]> wrote:
A bug has been filed:

http://jira.grails.org/browse/GRAILS-8535

What concerns me is that it keeps getting bumped.

I'm recommending we DON'T upgrade to Grails 2 until this gets fixed. And if
it doesn't get fixed soon, I'm recommending we re-write our application
using Ruby on Rails (over JRuby). Because that would be faster and better
than retrofiting our application to fit the testing framework.



--
View this message in context: http://grails.1312388.n4.nabble.com/Grails-2-Testing-Mock-once-always-mocked-tp4644441p4644576.html
Sent from the Grails - user mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email