I have a command object with a couple of static methods which access other domain objects.
static def isAValidSerial(String serialReference) {
return InventoryMaster.partSerialReferenceList(null, serialReference).size() > 0
}
This method is called by the validation constraints on the command object.
In a unit test I mock this behavior.
TransactionDetailCommand.metaClass.static.isAValidSerial = { String a -> println "mocked method called"; true }
before invoking some validation:
obj.validate(['serialReference'])
However this is throwing an exception where it didn't with 2.2.0.
The error is:
java.lang.IllegalStateException: Method on class [com.myStuff.MyClass] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
Despite the mocking when it's called from the validator it appears to be trying to invoke the real method and not the mock. If I put an explicit println before the obj.validate() it correctly prints the test text from the mocked method and returns the mocked value. This worked on 2.2 but now fails. I didn't used the grails update command. I created a new project and copied stuff to it. I've found various tips including removing the forked JVM stuff from the build config but nothing seems to work.
Anyone any ideas? Thanks.
I finally managed to get this working. Firstly I had to explicitly declare the collaborating class (InventoryMaster) within the #Mock annotation - on 2.2.n this wasn't required. Also when invoking mocked static methods from my validator I had to explicitly qualify the static call .. So what used to look like
if (!(cmdObj.transactionType.transactionIsAReceipt() || isAValidSerial(serialReference))) {
return 'TransactionDetailCommand.serialReference.not.found'
}
and work under 2.2.n had to be converted to ..
if (!(cmdObj.transactionType.transactionIsAReceipt() || TransactionDetailCommand.isAValidSerial(serialReference))) {
return 'TransactionDetailCommand.serialReference.not.found'
}
which kinda makes sense as I previously mocked the isAValidSerial using
TransactionDetailCommand.metaClass.static.isAValidSerial = {a -> println "running mock isAValidSerial -> true"; true }
Maybe that was 2.2.n being lax .. Anyway, my tests now work again !
Related
I'm using grails plugin multi-tenant-single-db. Within that context I need to write a spock test in which we temporarily remove the tenant restrictions. Location is my Tenant, so my method looks like this:
def loadOjectDetails(){
Location.withoutTenantRestriction{
// code here to retrieve specific items to the object to be loaded
render( template: "_loadDetails", model:[ ... ]
}
}
The method runs as expected, but trying to put method under test coverage the error output suggests that:
groovy.lang.MissingMethodException: No signature of method: com.myPackage.myController.Location.withoutTenantRestriction() is applicable for argument types:
and a stacktrace that stems on from there.
Do I need to Stub this? The withoutTenantRestriction is a wrapper around my entire method logic.
UPDATE:
The test code looks like this:
given:
params.id = 3002
currentUser = Mock(User)
criteriaSetup()
controller.getSalesOrder >> salesOrders[2]
when:
controller.loadOrderManageDetails()
then:
(1.._) controller.springSecurityService.getCurrentUser() >> currentUser
expect:
view == 'orderMange/orderManageDetail'
model.orderInstance == salesOrders[2]
Yes! You should be stubbing it as is created at run time not compile time.
You could stub it like below:
Your_Domain.metaClass.withoutTenantRestriction{Closure closure ->
closure.call()
}
This way your regular code will work in test cases. Also,as in withoutTenantRestriction it basically starts a new hibernate session, which doesn't matter much as now you have stubbed the closure, you could perform desired action in place of calling closure.call() only.
Also, same could be applied to withThisTenant.
In integration tests you don't need to stub it as is loading the whole environment.
Hope it helps!!
I am having issues while testing my grails controllers, as it depends on one service which seems not to be injected. I tried several ways (for ex. Extending classess like grailsunitestcase, specification) but I keep getting errors. The thing is that that service variable is null and I cant test my controller index method (which calls a render view) due to the exception...
I really need to know how to do this but I don't have a clue where to start...
Unit tests are just that. There is no grails 'environment' surrounding your controller. If the controller makes use of a service which is normally injected, you have to mock that service yourself.
#TestFor(SomeController)
#Mock([SomeService])
class SomeControllerSpec extends Specification
def "test some method"() {
given:
def mockService = mockFor(SomeService)
mockService.demand.someServiceMethod() { ->
return something
}
controller.someService = mockService.createMock()
when:
controller.someControllerMethod()
then:
// whatever checks are appropriate
}
}
In my tests I create a fake application per test method:
#Before
public void startFakeApplication() {
this.fakeApplication = fakeApplication();
start(this.fakeApplication);
}
#After
public void killFakeApplication() {
stop(this.fakeApplication);
this.fakeApplication = null;
}
Some of the tests use functionality that checks if the request is secure or not:
public boolean isHttps() {
Http.Request req = Controller.request();
return req.getHeader("x-forwarded-proto") != null
&& req.getHeader("x-forwarded-proto").contains("https");
}
That fails saying:
There is no HTTP Context available from here
Which is pretty strange, since it's running on a fake app, why can't it know that and create a fake request?
Oh well, I found this: Play framework 2.2.1: Create Http.Context for tests which introduced me to the mocking approach, so I was eager to give it a go and try to mock the Http.Context in the same way, the problem is that I can't seem to find the mock method...
In that thread he's using import static org.mockito.Mockito.* (which is where I assume the mock method is located) but I don't have that package, org.mockito only has one sub package named internal and I can't find any mock method there.
In the official documentation of Play! the only place talking about it is the Scala Test section and they use: import org.specs2.mock._ but there too I wasn't able to locate this mock method.
I'm using Play 2.2.2 (java).
Any ideas? Thanks.
I solved the same problem adding to my build.sbt the library dependency of Mockito:
libraryDependencies += "org.mockito" % "mockito-core" % "1.10.19"
Then I run play compile and play eclipse and magically the mockito library became available after refreshing the whole project in Eclipse.
And yes, mock() is a method of org.mockito.Mockito.
I had the same problem of Play not locating the mock function, and eventually realised that I hadn't extended my test class with Mockito;
import org.specs2.mock._
class TestClass extends Specification with Mockito
Just thought I'd add this as it has taken me ages to resolve and the above solution didn't work for me ......may save someone some time :)
I know there's a lot out there about this particular topic, however I can't quite find anyone who has stumbled across my issue, and hopefully someone can explain this to me.
I have a Domain where I use the injected grailsApplication's dynamic method 'isDomainClass' in the equals method:
#Override
public boolean equals(Object obj) {
if(!grailsApplication.isDomainClass(obj.getClass())) { return false }
...
}
This works fine, and to unit test this i do:
#Mock([MyDomain])
...
def mockGApp
void setUp() {
mockGApp = new Object()
mockGApp.metaClass.isDomainClass = { obj -> true }
}
...
void testSomething() {
def myDomain = new MyDomain()
myDomain.grailsApplication = mockGApp
....
}
And when I run this with test-app -unit (on command line or in STS) it passes just fine.
I then ave an integration test that uses that domain (no mocking this time) and that again runs fine when ran with test-app -integration (either on the command line or in STS)
However if i run 'test-app' so it does both at once, I get a MissingMethodException: no method signature isDomainClass exists with parameters (java.lang.Class) ... and all that jazz.
On investigating it with println's in the service I'm testing in the tests, in the integration portion of the testing, before the equals method of my domain class is called, I can quite happily call grailsApplication.isDomainClass() and get the desired affect. However when the code steps into the domain's equals function the isDomainClass() method no longer exists, despite the grailsApplication object referring to the same object which is referenced in the service and has the dynamically added method.
It appears that the dynamic methods that grails adds to this class are not being injected when its called within the domain's methods but are getting injected within the service. And more bizarrely this only happens if the integration tests follow the unit tests. If done separately, no problemo...
Where does this pollution stem from? IS there any way to solve it?
P.S. using Grails 2.1.0
You have to remove the class you modified from metaClassRegistry in the destroy method (i.e.after test case runs). See below:
#After
void destroy() {
GroovySystem.metaClassRegistry.removeMetaClass(MyDomain.class)
}
How do I setup logging in a grails unit-test?
When I try log.info or log.debug, the .txt output files are empty, even after I tried adding a console appender. What's going on here?
This might help, it's taken from the 1.2 release notes
By default, grails does not show the
output from your tests. You can make
it do so by passing -echoOut and/or
-echoErr to test-app:
grails test-app -echoOut -echoErr
If you extend GrailsUnitTestCase, you ought to be able to use mockLogging(), but the appenders you set up in Grails config won't be applied in a unit test, which works in isolation from the real framework. They'd only be available in integration tests.
I wasn't able to use mockLogging in grails 1.3.7 with GrailsUnitTestCase either. I think there is probably a bug and it may work in Grails 2.0. Here is what I did to work around it:
class Foo {
String name
Long invokeLogTest(String key) {
if (key.empty) {
log.error("key was sent as empty string")
return 10
}
}
}
void testErrorCase() {
def f = new Foo(name:'jp')
f.metaClass.log = [error:{}]
assert 10 == f.invokeLogTest("")
}