Is there any way to mock a file for unit testing in Grails?
I need to test file size and file type and it would help if I could mock these.
Any link to a resource would help.
You can mock java.io.File in Groovy code with Spock.
Here's an example of how to do it:
import spock.lang.Specification
class FileSpySpec extends Specification {
def 'file spy example' () {
given:
def mockFile = Mock(File)
GroovySpy(File, global: true, useObjenesis: true)
when:
def file = new File('testdir', 'testfile')
file.delete()
then :
1 * new File('testdir','testfile') >> { mockFile }
1 * mockFile.delete()
}
}
The idea is to return the file's Spock mock from a java.io.File constructor call expectation which has the expected parameters.
Related
Somewhere in my shared library I got a helper class like this:
class Helper {
def script
Helper(script) {
this.script = script
}
void sendTemplate(String webhook, String template, Map<String, String> values, TemplateMapper mapper) {
def body = mapper.map(template, values)
def resp = script.httpRequest(contentType: 'APPLICATION_JSON', httpMode: 'POST',
requestBody: body, url: webhook)
if (resp.status != 200) {
throw new UnableToNotifyException()
}
}
}
I'm trying to test said class like so:
class HelperSpec extends JenkinsPipelineSpecification {
def helper
def setup() {
helper = new Helper(this)
}
def "a test"() {
setup:
def webhook = 'aWebhook'
def template = '%replaceMe'
def values = ['%replaceMe': 'hello world!']
def mapper = new SimpleTemplateMapper()
getPipelineMock('httpRequest')(_) >> [status: 200]
when:
helper.sendTemplate(webhook, template, values, mapper)
then:
1 * getPipelineMock('httpRequest')(_)
}
}
I'm using gradle and my build.gradle file has
testImplementation 'org.jenkins-ci.plugins:http_request:1.10#jar'
Other steps' tests run perfectly but with this one I always get
java.lang.IllegalStateException: There is no pipeline step mock for [httpRequest].
1. Is the name correct?
2. Does the pipeline step have a descriptor with that name?
3. Does that step come from a plugin? If so, is that plugin listed as a dependency in your pom.xml?
4. If not, you may need to call explicitlyMockPipelineStep('httpRequest') in your test's setup: block.
And when I use explicitlyMockPipelineStep('httpRequest') I get a null pointer exception, because, I presume, the default mock returns a null.
Is there anything I'm missing in the test to get it working? Thanks in advance!!!
I've recently upgraded to grails 3.3.1 and realised that grails.test.mixin.Mock has been pulled to separate project which has been build just for backward compatibility according to my understanding org.grails:grails-test-mixins:3.3.0.
I've been using #Mock annotation to mock Grails service injected into groovy/src class under test. What is the tactic to mock collaborating services in this case? Is there anything from Spock what I can use or should I fallback to grails-test-mixins plugin?
Class under test:
import gra
ils.util.Holders
import grails.util.Holders
class SomeUtilClass {
static MyService myService = Holders.grailsApplication.mainContext.getBean("myService")
static String myMethod() {
// here is some code
return myService.myServiceMethod()
}
}
My test spec (Grails 3.2.1):
import grails.test.mixin.Mock
import spock.lang.Specification
#Mock([MyService])
class ValidatorUtilsTest extends Specification {
def 'my test'() {
when:
def result = SomeUtilClass.myMethod()
then:
result == "result"
}
}
Due to you use Holders.grailsApplication in your SomeUtilClass, you can try to add #Integration annotation:
import grails.testing.mixin.integration.Integration
import spock.lang.Specification
#Integration
class ValidatorUtilsTest extends Specification {
def 'my test'() {
when:
def result = SomeUtilClass.myMethod()
then:
result == "result"
}
}
Not sure, but hope it work for you.
Try with this code
#TestMixin(GrailsUnitTestMixin)
#Mock([your domains here])
class ValidatorUtilsTest extends Specification {
static doWithSpring = {
myService(MyService)
}
def 'my test'() {
when:
def result = SomeUtilClass.myMethod()
then:
result == "result"
}
}
Remove the #Mock annotation and implement ServiceUnitTest<MyService> in your test class.
Service:
#GrailsCompileStatic
class MyService {
final static String PLACEHOLDER = '<x>'
#Value('${myService.url}') // Suppose it http://example.com/doc-<x>.html
private String urlTemplate
String getSomeUrl(String lang) {
urlTemplate.replace(PLACEHOLDER, lang)
}
}
Unit test:
#TestFor(MyService)
class MyServiceSpec extends Specification {
#Unroll
void "test get some url for #lang"() {
when:
def someUrl = service.getSomeUrl(lang) // NullPointerException, because urlTemplate is null
then:
someUrl.endsWith(lang + '.html')
where:
lang << ['es', 'en']
}
}
So, as I mentioned above, urlTemplate is null (but config value exists in .properties). How to fix it?
Solution:
class MyServiceSpec extends IntegrationSpec {
MyService myService
#Unroll
void "test get some url for #lang"() {
when:
def someUrl = myService.getSomeUrl(lang)
then:
someUrl.endsWith(lang + '.html')
where:
lang << ['es', 'en']
}
}
Unit tests are used to test isolated units of code. If you are testing behavior that is dependent on the configuration value, inject it into the unit test to achieve reusable unit tests.
On the other hand, if you are testing that the variable is actually set or what the variable is set to, you need to use an integration test because you are basically testing your integration with Grails' configuration mechanism: http://docs.grails.org/latest/guide/testing.html#integrationTesting
As a third option, you could also use functional testing to verify that in the end everything appears to function as it is supposed to: http://docs.grails.org/latest/guide/testing.html#functionalTesting
How to bind #Value annotated fields of service in unit tests?
One way to do it...
#TestFor(MyService)
class MyServiceSpec extends Specification {
#Unroll
void "test get some url for #lang"() {
when:
service.urlTemplate = 'some value'
def someUrl = service.getSomeUrl(lang)
then:
someUrl.endsWith(lang + '.html')
where:
lang << ['es', 'en']
}
}
In case this helps someone.
I had an issue where a missing property variable used in a #Value was giving me an BeanExpressionException for a service unit test. I found that setting that variable in my application.yml for the test environment solved my problem in Grails 4.
#Value in question:
#Value("#{\${some_property_here}}") public Map<String, Map<String, Integer>> SOME_MAP_OF_MAPS
How does one integration-test (Grails) a controller with no inputs and a render view? Here is my controller code and test; the test looks correct but throws a "java.lang.Exception: No tests found matching grails test target pattern filter" error. many thanks for your help. -ryan
Controller code section:
class ReportSiteErrorsController {
static allowedMethods = [reportError: 'GET', saveReportError: 'POST']
def mailService
#Transactional(readOnly = true)
def reportError() {
render(view:'reportError', model:[])
}
}
Integration Test:
#TestFor(ReportSiteErrorsController)
class ReportSiteErrorsControllerTests extends DbunitGroovyTestCase {
#Test
void "test report error"() {
controller.reportError()
assert view == "reportError"
}
}
Leave the extension off in your command line. You are supposed to be specifying the name of the test, not the name of the file in which it is defined.
Use something like...
grails test-app integration: NameOfTest
Not something like...
grails test-app integration: NameOfTest.groovy
I hope that helps.
in Grails 2.4 the default syntax to test render "hello" would be:
import grails.test.mixin.TestFor
import spock.lang.Specification
/**
* See the API for {#link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
*/
#TestFor(SimpleController)
class SimpleControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
controller.index()
expect:
response.text == "hello"
}
}
The assert won't do for me. See also: the Grails Documentation
Within a Spock unit test, I am trying to test the behaviour of a method findRepositoriesByUsername independent of getGithubUrlForPath, both belonging to the same service.
Repeated attempts to use the metaClass have failed:
String.metaClass.blarg produces an error No such property: blarg for class: java.lang.String
service.metaClass.getGithubUrlForPath to modify the service instance doesn't work
GithubService.metaClass.getGithubUrlForPath to modify the service class doesn't work
Tried adding/modifying methods on the metaClass in the test methods' setup and when blocks, neither worked as expected
The test:
package grails.woot
import grails.test.mixin.TestFor
#TestFor(GithubService)
class GithubServiceSpec extends spock.lang.Specification {
def 'metaClass test'() {
when:
String.metaClass.blarg = { ->
'brainf***'
}
then:
'some string'.blarg == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { pathParts ->
requestPathParts = pathParts
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
}
The service:
package grails.woot
import grails.converters.JSON
class GithubService {
def apiHost = 'https://api.github.com/'
def findRepositoriesByUsername(username) {
try{
JSON.parse(getGithubUrlForPath('users', username, 'repos').text)
} catch (FileNotFoundException ex) {
// user not found
}
}
def getGithubUrlForPath(String ... pathParts) {
"${apiHost}${pathParts.join('/')}".toURL()
}
}
I've tested the String.metaClass.blarg example in the groovy shell (launched by grails), and it did as expected.
Do I have a fundamental misunderstanding here? What am I doing wrong? Is there a better way to handle the desired test (replacing a method on the service under test)?
This is how the tests can be written to make them pass:
def 'metaClass test'() {
given:
String.metaClass.blarg = { -> 'brainf***' }
expect:
// note blarg is a method on String metaClass
// not a field, invoke the method
'some string'.blarg() == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { String... pathParts ->
requestPathParts = pathParts
[text: 'blah'] // mimicing URL class
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
Why don't you use Spock's great Mocking abilities?
Look at http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_creating_mock_objects
There is no need to peek inside metaclass itself, you can create some stub object, and demanded method will be called instead of original one. Also you can use Groovy's MockFor and StubFor, they can be a little bit easier.
You cannot fully trust metaclass inside spock tests specification.
There is some complex logic inside it, which can easyly mess thing's up. Try run some tests under debugger, and you will see it.
Spock uses metaclasses under the hood. It can override your own try's.