问题
I have a Grails service that sends out e-mails using a 3rd-party service by doing a HTTP call:
class EmailService {
def sendEmail(values) {
def valueJson = values as JSON
... // does HTTP call to 3rd party service
}
}
I've written a unit test to test this service (because an integration test spins up Hibernate and the entire domain framework, which I don't need):
@TestFor(EmailService)
class EmailServiceTests {
void testEmailServiceWorks() {
def values = [test: 'test', test2: 'test2']
service.sendEmail(values)
}
}
However, when I execute this unit test, it fails with this exception when it tries to do the as JSON
conversion:
org.apache.commons.lang.UnhandledException: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Unconvertable Object of class: java.util.LinkedHashMap
I then re-wrote my unit test to just do the following:
void testEmailServiceWorks() {
def value = [test: 'test', test2: 'test2']
def valueJson = value as JSON
}
And I get the same exception when it tries to do the as JSON
conversion.
Does anyone know why I'm getting this exception, and how I can fix it?
回答1:
The as JSON magic is created when the domain framework spins up.
You have to either change your test to an integration one or mock the asType.
def setUp(){
java.util.LinkedHashMap.metaClass.asType = { Class c ->
new grails.converters."$c"(delegate)
}
}
Rember to clean up after yourself in the tearDown, you wouldn't want metaprogramming leaks in your test suite.
def tearDown(){
java.util.LinkedHashMap.metaClass.asType = null
}
Edit: If you come from the future, consider this answer: https://stackoverflow.com/a/15485593/194932
回答2:
Even though you are testing a service, you can apply the @TestMixin(ControllerUnitTestMixin)
annotation to your test class to get Grails to set up the JSON converter.
回答3:
you can initialise the JSON in the setUp() . There are various marshallers which implement ObjectMarshaller , which need to be added to the ConverterConfiguration for JSON conversion to work.
http://grails.github.io/grails-doc/2.4.4/api/index.html?org/codehaus/groovy/grails/web/converters/marshaller/json/package-summary.html
example :
DefaultConverterConfiguration<JSON> defaultConverterConfig = new DefaultConverterConfiguration<JSON>()
defaultConverterConfig.registerObjectMarshaller(new CollectionMarshaller())
defaultConverterConfig.registerObjectMarshaller(new MapMarshaller())
defaultConverterConfig.registerObjectMarshaller(new GenericJavaBeanMarshaller())
ConvertersConfigurationHolder.setTheadLocalConverterConfiguration(JSON.class, defaultConverterConfig);
回答4:
As Grails 3.3.x grails-test-mixins
plugin is deprecated. @see migration guide.
For this problem you should implement GrailsWebUnitTest
which is coming from Grails Testing Support Framework.
回答5:
I was getting the same error when trying to unit test a controller that calls "render myMap as JSON". We use Grails 1.3.7 and none of the other solutions worked for me without introducing other problems. Upgrading Grails was not an alternative for us at the moment.
My solution was to use JSONBuilder instead of "as JSON", like this:
render(contentType: "application/json", {myMap})
See http://docs.grails.org/latest/guide/theWebLayer.html#moreOnJSONBuilder
(I realize this is old, but came here in search for a solution and so might others)
回答6:
I just ran into this, and I really didn't want to implement GrailsWebUnitTest
as recommended in another answer here. I want to keep my service test as "pure" and lean as possible. I ended up doing this:
void setupSpec() {
defineBeans(new ConvertersGrailsPlugin())
}
void cleanupSpec() {
ConvertersConfigurationHolder.clear()
}
This is how it happens under the hood when you implement GrailsWebUnitTest
(via WebSetupSpecInterceptor
and WebCleanupSpecInterceptor
).
That said, the converters seem to be meant for use in the web tier, primarily for making it easy to transparently return data in different formats from a controller. It's worth considering why the service you're testing needs the converters in the first place.
For example, in my case, someone used the JSON converter to serialize some data to a string so it could be stored in a single field in the database. That doesn't seem like an appropriate user of the converters, so I plan on changing how it's done. Making the converters available in my service test is a temporary solution to allow me to improve our test coverage before I refactor things.
来源:https://stackoverflow.com/questions/11785708/how-do-i-unit-test-a-grails-service-that-uses-a-converter