I am running my Junit test cases for my groovy class using spock framework I am using Mock to invoke my class. but it is giving me MissingMethodException but if I invoke the same method by normal creating object def obj = new MyClass() way it is working. please let me know am I missing something? below is my stacktrace
Expected no exception to be thrown, but got 'groovy.lang.MissingMethodException'
at spock.lang.Specification.noExceptionThrown(Specification.java:119)
at .AsplTest.fetchXmlTest(AsplTest.groovy:35)
Caused by: groovy.lang.MissingMethodException: No signature of method: com.webservice.Service.fetchAsplXml() is applicable for argument types: (java.lang.String, groovy.net.xmlrpc.XMLRPCServerProxy, java.lang.String) values: [3c98fa0dd1b5d92af599779bfb7be655, groovy.net.xmlrpc.XMLRPCServerProxy#797b0699, ...]
Possible solutions: getFetchAsplXml()
at .AsplTest.fetchXmlTest(AsplTest.groovy:33)
below is my test code
public void fetchXmlTest() {
given:
def asplObject=Mock(Service);
when:
asplObject.fetchXml(sessionId, serverProxy, "https://serverproxy")
then:
noExceptionThrown()
}
FYI:
my groovy version is 2.4.12 and spock version 1.1-groovy-2.4
It seems to me that you are doing things backwards.
Mocks are not test subjects. They are used to control the interactions of your test subjects with other objects. It looks, from the code you posted, that you want to test the invocation of method fetchXml on your Service object.
To do this, you need to create an instance of Service, and call the method. If your Service has collaborating objects, then you can Mock them, and add interactions, like this:
given:
def service = new Service()
and:
service.collaboratingObject = Mock(CollaboratingObjectClass)
when:
service.getFetchAsplXml()
then:
1 * service.collaboratingObject.someMethodReturningAString(_ as String) >> {String input-> "mockedResult from $input" as String }
Related
I want to run a unit test for a service. The method I want to test includes a some log.debug() statements. While the log property is injected at runtime, it does not seem to be injected in tests, so it throws groovy.lang.MissingPropertyException: No such property: log for class:
This is my unit test class:
#TestFor(ServiceUnderTest)
#Mock([ServiceUnderTest])
class ServiceUnderTestTests {
def test() {
def mock = [ mockedProp: [...] ] as ServiceUnderTest
def info = mock.doOperation()
assert ....
}
}
I've also tried adding MockUtils.mockLogging(ServiceUnderTest) but with no success.
How can I get the log property properly injected in my service class while in unit tests?
You do not have to have the test class annotated with #Mock([ServiceUnderTest]). #TestFor(ServiceUnderTest) detects its a service class and does all the mocking automatically. It also adds a service property to the test class that can be accessed in all the test methods and mocks the log property accordingly.
I think the problem why neither mocking nor explicit log mocking with MockUtils.mockLogging(ServiceUnderTest) does work in your case is the as coercion you are using in your test method code:
def mock = [ mockedProp: [...] ] as ServiceUnderTest
Groovy internally uses java.lang.reflect.Proxy to create a proxy descendant class from ServiceUnderTest. The proxy class does not see changes done to the ServiceUnderTest meta class like the added log property.
I would solve this issue by using a per-object meta class. You can mock the property getter (or setter) by altering the metaClass of the service object. Be aware that meta-class changes are rolled back by Grails in-between execution of test methods:
service.metaClass.mockedProp = {-> ... }
In my unit test, I mock a service (which is a ref of the class under test).
Like:
given:
def mockXxService = mockFor(XxService)
mockXxService.demand.xxx(1) {->}
service.xxService = mockXxService
when:
service.yyy()
then:
// verify mockXxService's xxx method is invoked.
For my unit test, I want to verify that mockXxService.xxx() is called. But grails document's mockControl.verify() doesn't work for me. Not sure how to use it correctly.
It is very similar to mockito's verify method.
Anyone knows it?
You are using spock for your unit test, you should be easily able to use spock's MockingApi check invocations:
given:
def mockXxService = Mock(XxService)
service.xxService = mockXxService
when:
service.yyy()
then:
1 * mockXxService.xxx(_) //assert xxx() is called once
You could get more insight about mocking from spockframework docs.
You can even stub and mock that while mocking the concerned service as:
def mockXxService = Mock(XxService) {
1 * xxx(_)
}
If you want Mockito-like behavior in Grails unit tests - just use Mockito. It is far more convenient than Grails' mocking methods.
In one of my unit tests, I am having some difficulty getting a mocked method to executed. I have the following test code:
void testExample() {
def mockICFService = new MockFor(ICFService)
...
//Mock the methods
controller.metaClass.icfList = { def person ->
println "icfList"
return [new IC(conceptId:'12345')]
}
mockICFService.demand.getAllIC(1..1) { def id, def withHist, def appId ->
println "mocking service"
return new Person()
}
...
def model = controller.detail()
}
Inside of detail in my controller class I create a Person via the ICFService's getAllIC(). This part works correctly. Later in the function, however, there is a call to icfList (which is defined in the controller). Through println's I have determined that the call is still being made, although it is returning an empty array. I believe that this is because the array is populated based on data in the servletContext, but in Unit Testing there is no access to that (hence my trying to mock it out).
Does anyone know how to force the test to use the mocked version of controller.icfList instead of calling the actual method in controller?
When I try your code, what blows up for me is the mocked service, and the part that works properly is the mocked-out icfList() method. The opposite of your observation, interestingly. For what it's worth, here's what I did:
First replace new MockFor() class instantiation with the mockFor() method. Then you need to inject the mock service into the controller.
def mockICFService = mockFor(ICFService)
controller.iCFService = mockICFService.createMock()
By doing the above, only the mocked versions of icfList() and getAllIC() get called, so you are not using the servletContext at all. Check out the Grails testing documentation for more info.
I have some often-used helper methods for unit tests put into a seperate file. The idea is to, for example, allow my XYZTests.groovy to call TestHelper.getUserObject() in order to get a fully initialized instance of User.
Now the problem is, that there's a springSecurityService.encodePassword(pw) being called in the User's beforeInsert() which always fails as there's no mock for springSecurityService in TestHelper.groovy.
java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
In User.groovy:
def beforeInsert() {
// ...
password = springSecurityService.encodePassword(pw)
// ...
}
Note: I would like to avoid any mocking in TestHelper.groovy in order to use it's methods in integration tests too.
In spite of that, even if I try to call a mockFor() anywhere in the TestHelper.groovy, I get an MME:
No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
groovy.lang.MissingMethodException: No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
at myproject.TestHelper.mockSpringSecurityService(TestHelper.groovy:59)
at myproject.TestHelper$mockSpringSecurityService.callStatic(Unknown Source)
at myproject.TestHelper.getUserObject(TestHelper.groovy:47)
at myproject.TestHelper$getUserObject.call(Unknown Source)
at myproject.UserTests.setUp(UserTests.groovy:26)
Note: I currently mock the springSecurityService.encodePassword like this:
// in UserTests.groovy
protected void setUp() {
// mockDomain(...) and such here
def u = TestHelper.getUserObject("Pummel")
u.springSecurityService = mockSpringSecurityService()
assert u.save()
}
private mockSpringSecurityService() {
def ssService = mockFor(SpringSecurityService,true)
ssService.metaClass.encodePassword() { password ->
"08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6"
}
ssService
}
Any ideas on how I may be able to use the helper class while leaving all mocking in the unit tests only?
Where would I best place the TestHelper.groovy file for using it in integration AND unit tests?
Note that everything is working perfectly fine when I move all helpers into UserTests.groovy directly!
The solution to this is to refrain from calling any user.save() in the TestHelper.groovy.
This makes sense, as for many (unit) tests a persisted (saved) instance is unnecessary anyways.
On the other hand many cases actually require an unsaved intance. (In order to test certain effects of the .save() itself, for example)
A working example for integration tests would be:
def user = TestHelper.getUserObject()
user.save()
For a unit tests:
def user = TestHelper.getUserObject()
user.springSecurityService = new SpringSecurityService() // or the described mock accordingly
user.save()
This keeps any mocks out of TestHelper.groovy
In your TestHelper you can use Groovy ExpandoMetaClass metaClass.static to slap a mock closure for encodePassword on SpringSecurityService:
SpringSecurityService.metaClass.'static'.encodePassword = {'08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6'}
I would stick this class in a test package under src/groovy
I'm writing a Spock Spec (unit test) for a Service in Grails 1.3.5, and I've run across the following error:
No signature of method: myapp.Thing.findAll() is applicable for argument types: (java.util.LinkedHashMap) values: [[sort:index, order:asc]] Possible solutions: findAll(), findAll(groovy.lang.Closure), find(groovy.lang.Closure), getAll(java.util.List), getAll([Ljava.lang.Object;)
groovy.lang.MissingMethodException: No signature of method: myapp.Thing.findAll() is applicable for argument types: (java.util.LinkedHashMap) values: [[sort:index, order:asc]]
Possible solutions: findAll(), findAll(groovy.lang.Closure), find(groovy.lang.Closure), getAll(java.util.List), getAll([Ljava.lang.Object;)
at grails.test.MockUtils.addDynamicFinders_closure56(MockUtils.groovy:641)
at myapp.MyService.getCards(MyService.groovy:8)
at myapp.MyServiceSpec.getCards returns empty map if no cards or statuses are available(MyServiceSpec.groovy:13)
Previously this test passed, but the failure occurred when I modified my Service to include sorting of results in the getThings() method.
class MyService {
static transactional = true
static getThings() {
Thing.findAll(sort: 'index', order: 'asc')
}
}
This still appears to work when the application is run, so I suspect it's a limitation of the implementation of mockDomain().
class MyServiceSpec extends UnitSpec {
def 'mockDomain has some limits, i suspect'() {
given:
mockDomain(Thing)
def myService = new MyService()
when:
myService.getThings()
then:
true
}
}
So my question is are their differences in the methods added to a domain class using mockDomain() as opposed to using the real domain class at runtime? If so, what are they?
sort and order are Hibernate criteria parameters, they won't work with MockDomain() - it doesn't involve Hibernate. Luckily.
You can mock that findAll() signature yourself, using instances array - the second parameter of MockDomain(), (EDIT) like, this overrides findAll(Map) signature of Thing:
List<Thing> thingInstances = []
void setUp() {
mockDomain(Thing, thingInstances)
Thing.metaClass.`static`.findAll = { Map m ->
def result = thingInstances.sort{ it."${m.order}" }
m.order == 'asc' ? result : result.reverse()
}
}
(EDIT end)
Or you can make it integration test, then it will run for ages. Which I don't recommend.
There is a new approach for mocking domain objects: http://grails.1312388.n4.nabble.com/New-approach-to-mocking-domain-classes-in-Grails-unit-tests-td2529895.html. Maybe this helps you out here.