2
votes

I have some (non-Grails-artifact) classes that access the service layer beans via passing around the grailsApplication object. However I'm having trouble unit testing the classes implemented in this way. Why doesn't the bean get registered in the main context?

@TestMixin(GrailsUnitTestMixin)
class ExampleTests {
  void setUp() {}

  void tearDown() {}

  void testSomething() {
    defineBeans {
      myService(MyService)
    }

    assert grailsApplication.mainContext.getBean("myService") != null
  }
}

The above code fails with:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myService' is defined

What I'm trying to do is access services from plain old Java classes via the grailsApplication. This works, but not in unit test environment. Should I do it differently?

class POJO {
  MyService myService;

  public POJO(GrailsApplication grailsApplication) {
    myService = (MyService) grailsApplication.getMainContext().getBean("myService");
  }
}
3

3 Answers

9
votes

The answer is that in the GrailsUnitTestMixin the applicationContext that holds your beans is set as the parentContext in the grailsApplication

beans.registerBeans(applicationContext)

static void initGrailsApplication() {
...
//the setApplicationContext in DefaultGrailsApplication set's the parentContext
grailsApplication.applicationContext = applicationContext
}

So you can get your beans with:

defineBeans {
  myService(MyService)
}

assert applicationContext.getBean("myService")
assert grailsApplication.parentContext.getBean("myService")

EDIT

Today I faced the same problem, and my solution is:

@Before
void setup() {
  Holders.grailsApplication.mainContext.registerMockBean("myService", new MyService())
}
7
votes

In my case (grails 2.4.4) the accepted solution didn't work but pointed me in the right direction, this line worked instead as the bean factory in the mainContext within my unit test was an OptimizedAutowireCapableBeanFactory

Holders.grailsApplication.mainContext.beanFactory.registerSingleton('myBean', new MyBeanClass())
2
votes

I have spent some time with the same issue, in my case running grails 2.2.4 and having (in src/groovy):

import grails.util.Holders
class SomeClass {
  transient myService = Holders.grailsApplication.mainContext.getBean 'myService'
  .....
}

Which is a bit different to question author, but at least it will be useful for someone coming from search engine results

Nevertheless accepted answer did not work for me, so I came up with a bit different approach of mocking and registering service used in SomeClass.

import grails.util.Holders
.. other imports
@TestMixin(GrailsUnitTestMixin)
class SomeClassTests {
    @Before
    void setUp() {
        Holders.grailsApplication = grailsApplication
        defineBeans {
            myService(MyServiceMock)
        }
    }
    ....
}

class MyServiceMock extends MyService {
  // overriden methods here
}