Unit testing service returns a 'cannot add service class' error message - unit-testing

in my services folder, i have this file
abstract class AsyncUploadedFileService<T> {
def grailsApplication
def jesqueService
String jobName
String workerPool
String _queueName
AsyncUploadedFileService (Class<T> job, String workerPool) {
jobName = job.simpleName
this.workerPool = workerPool
}
}
and I have a unit test
#Build(UploadedFile)
#TestMixin(GrailsUnitTestMixin)
#TestFor(AsyncUploadedFileService)
class AsyncUploadedFileServiceSpec extends Specification {
def setup() {
User.metaClass.encodePassword = {-> }
}
void "add to the worker pool"() {
when:
UploadedFile uploadedFile = UploadedFile.build()
then:
service.perform(uploadedFile)
}
}
when I run my test, I get the following error
Cannot add Service class [class com.example.AsyncUploadedFileService]. It is not a Service!
org.codehaus.groovy.grails.exceptions.GrailsConfigurationException: Cannot add Service class [class com.example.AsyncUploadedFileService]. It is not a Service!
at grails.test.mixin.services.ServiceUnitTestMixin.mockService(ServiceUnitTestMixin.groovy:46)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.runtime.extension.builtin.JUnitFixtureMethodsExtension$FixtureType$FixtureMethodInterceptor.intercept(JUnitFixtureMethodsExtension.java:145)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:84)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
Is this error because I'm using the service.perform() syntax? If so, what other way is there of testing the AsynchUploadedFileService function?

You can't instantiate an abstract class, so you can't create one to test with. Make it non-abstract, or create a concrete subclass and test that.
Also, since you have a constructor with arguments, there is no default (no-arg) constructor. If you have no constructors, the compiler adds one for you (in both Java and Groovy) and in Grails since services are auto-registered, you must have a no-arg constructor (you can have others, but I can't see why you would) so Spring can create the instance. You can always manually wire up a class as a Spring bean and provide constructor args in resources.groovy, but then it wouldn't be a Grails service, so it would be better to put it in src/groovy.

Related

Mock is giving missing method exception in spock framewrok

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 }

Grails Unit Test Service MissingProperty 'log'

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 = {-> ... }

Mock a method for Grails Unit Test

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.

How can I correctly move grails unit-test helper methods into a seperate file?

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

Mocking the "save" method on domain classes

I'm having hard time mocking the save instance method in my unit tests in Grails 1.3.3. I've created a simple domain class named Person, it has one property (nullable) called "name".
package tutorial
class Person {
String name
static constraints = {
name nullable: true
}
}
In my test I'm trying to do something I've found in the documentation:
class PersonTests extends GrailsUnitTestCase {
public void testCanSavePerson() {
def testInstances = []
mockDomain(Person, testInstances)
assertEquals(0, Person.count())
new Person(name: "Bob").save()
assertEquals(1, Person.count())
}
}
However as soon as I run the test what I get is an exception:
java.lang.NullPointerException
at grails.test.MockUtils$_addValidateMethod_closure83.doCall(MockUtils.groovy:973)
at grails.test.MockUtils$_addValidateMethod_closure84.doCall(MockUtils.groovy:1014)
at grails.test.MockUtils$_addDynamicInstanceMethods_closure67.doCall(MockUtils.groovy:736)
at grails.test.MockUtils$_addDynamicInstanceMethods_closure67.doCall(MockUtils.groovy)
at tutorial.PersonTests.testCanSavePerson(PersonTests.groovy:25)
whereas the line 25 is exactly the one that calls save() on newly created instance.
Does anyone knows what am I doing wrong?
This is an already known bug in Grails 1.3.3. Read more about it and find a workaround in the associated JIRA ticket GRAILS-6482.