I have an integration test where I sometimes want to mock the return of a service method. However, I have seen that once I mock that method, the subsequent tests that call it will also use the mocked function.
Is this normal? If so, how can I have test which sometimes use mocked functions and sometimes use the real implementation?
Here is my code:
MyController {
def someService
def save(){
...
def val = someService.methodToMock()//sometimes want to mock other times, not
...
}
}
MyTest {
def "test 1"(){
...
//I want to mock here
myController.someService.metaClass.methodToMock = { [] }
...
myController.save()
}
def "test 2"(){
...
//I don't want to mock here, however
// it is returning the mocked results
myController.save()
}
}
In general you don't want to change anything to do with metaclasses in integration or functional tests, only in unit tests. It's expected that you'll be doing this in unit tests and there's automatic support for restoring the original metaclass after each test or after each test class runs depending on the version of Grails and how things are configured. But this isn't the case in integration tests.
There are several different approaches you can use. If you use untyped dependency injection, e.g. def someService, then you can overwrite the real service instance with anything you want, and as long as it has the method(s) that you'll be invoking during the test method the controller won't know or care that it's not the real service.
I like to use a map of closures in this case, since Groovy will invoke a closure as if it were a method. So for 'test 1' you could do this:
def "test 1"() {
...
def mockedService = [methodToMock: { args -> return ... }]
myController.someService = mockedService
...
myController.save()
}
This works because you get a new instance of the controller for each test, and you change the service just for that instance, but the real service isn't affected at all.
Your controller invokes someService.methodToMock(), which is actually someService.get('methodToMock').call(), but the map access and closure invocation syntax can take advantage of Groovy's syntactic sugar to look like a regular method call.
Another option is to subclass the service and override the method(s) that you want, and replace the injected instance with that. This or something like it would be necessary if you type the dependency injection (e.g. SomeService someService). Either create a named subclass (class TestSomeService extends SomeService { ... }) or create an anonymous inner class:
def "test 1"() {
...
def mockedService = new SomeService() {
def methodToMock(args) {
return ...
}
}
myController.someService = mockedService
...
myController.save()
}
Altering the metaClass in one test will absolutely affect other tests. You're altering the groovy system, and need to perform some special cleanup if you're metaClassing. At the end my methods where I metaClass, I call a function to revoke the metaClass changes, passing in the name of the class that was metaClassed, and the instance metaClassed if there was one.
def "some authenticated method test"() {
given:
def user = new UserDomain(blah blah blah)
controller.metaClass.getAuthenticatedUser = { return user }
when:
controller.authenticatedMethod() // which references the authenticated user
then:
// validate the results
cleanup:
revokeMetaClassChanges(theControllerClass, controller)
}
private def revokeMetaClassChanges(def type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if (instance != null) {
instance.metaClass = null
}
}
Alternatively, you can just mock the service in the test. A method similar to that mentioned by Burt could be:
def "some test"() {
given:
def mockSomeService = mockFor(SomeService)
mockSomeService.demand.methodToMock(1) { def args ->
return []
}
controller.someService = mockSomeService.createMock()
when:
controller.save()
then:
// implement your validations/assertions
}
Related
In my Jenkins shared library, I have tons of groovy files in the /vars dir that define custom steps.
Many of them have multiple methods defined, and one method in a file may call another in the same file.
I am looking for a way to mock these local methods, so I can unit test each method, specifically those that call the others, without actually invoking them.
Say the structure is like this:
// vars/step.groovy
def method1() {
def someVar
result = method2(someVar)
if (result) {
echo 'ok'
}
else {
echo 'no'
}
}
def method2(value) {
if (value == 1) {
return true
}
else {
return false
}
}
Obviously this is a very simplified example. But what I need is a way to mock method2 so that I can test method1 with result of both true and false, without actually invoking method2.
I have tried the helper.registerAllowedMethod pattern, but that doesn't seem to apply to local methods. I've tried Mockito and Spock but they seem like overkill for what I need, and too much change to inject for simple cases.
I've also tried defining the methods locally in the test script with mock closures, but I can't find the right place to do that and/or the correct syntax.
I hope there's a way to do something like this:
// test/com/myOrg/stepTest.groovy
import org.junit.*
import com.lesfurets.jenkins.unit.*
import com.lesfurets.jenkins.unit.BasePipelineTest
import static groovy.test.GroovyAssert.*
class stepTest extends BasePipelineTest {
def step
#Before
void setUp() {
super.setUp()
step = loadScript("vars/step.groovy")
}
#Test
void method1Test_true () {
helper.registerAllowedMethod('method2', [], { true }
result = step.method1()
assert 'ok' == result
}
#Test
void method1Test_false () {
helper.registerAllowedMethod('method2', [], { false }
result = step.method1()
assert 'no' == result
}
}
UPDATE: one thing i've recently noticed is, in the stacktrace, the method2 local function is not listed. It's as if the local function is instantiated "inline" or in some way that it is not actually a new call, the execution just flows into it. I don't know the technical term. But it explains why the mock of method2 never gets hit: it is never called.
method1.call()
method1.successfullyMockedExternalFunction()
// i would expect method2 to be here, but it's not - the next stack items are functions _inside_ method2.
method1.functionInsideMethod2()
UPDATE 2:
This got some attention in the JPU GitHub repo.
Here's a unit test that works fine.
#Subject([WeatherServiceImpl.class,URLConnection.class])
class WeatherServiceImplSpec extends Specification{
def "First spock test I ever wrote"(){
given: "some mock objects"
//1. define mock HttpURLConnection object
def mockConnObj=Mock(HttpURLConnection.class)
//2. defn of another mock
def mockURLAdaptor=Mock(URLAdapter)
when: "define some calls"
def test=new WeatherServiceImpl(mockURLAdaptor)
test.run("Raleigh")
then: "make some assertions"
1*mockURLAdaptor.openConnection(_)>>mockConnObj
1*mockConnObj.getResponseCode()
}//end def test
}
What I don't understand is that if I do this in the 'given' block:
def mockURLAdaptor = Mock(URLAdapter) >> {
openConnection(_) >> mockConnObj
}
then the method stub doesn't actually return the mock connection object as intended. To me, this is the more natural flow of expressions. Doing the same thing in the 'then' block, however, works as intended. Not sure what's going on here. Can't seem to find a relevant discussion on the web. I may also post this on stackoverflow.
Here's the class under test:
package com.icidigital.services.impl
import com.icidigital.Helpers.URLAdapter
import com.icidigital.services.IWeatherService
public class WeatherServiceImpl implements IWeatherService {
private URLAdapter urlAdapter;
private URLConnection urlConn;
public WeatherServiceImpl(URLAdapter urlAdapter){
//injecting this dependency, so I can unit test
//by injecting a mock URLAdapter instance. In
//normal operation, urlAdaptee would be an instance
//of URLWrapper, which simply wraps around the
// URL class, which is a final class and cannot
// be mocked normally.
this.urlAdapter=urlAdapter;
}
public String run(String city){
...
..
urlConn=urlAdapter.openConnection(city);
//(throws a null pointer exception while spock-ing)
urlConn.setRequestMethod("GET");
}
}
And here's the url adapter that exposes the method: openConnection. In the running code, there is a class URLWrapper that simply wraps around the java.net.URL class. I needed to do this to get around the fact that I couldn't directly mock the java.net.URL class since it is a final class.
interface URLAdapter {
public HttpURLConnection openConnection(String cityName);
}
If you want to return some object you should use a Stub instead.
#Subject([WeatherServiceImpl.class,URLConnection.class])
class WeatherServiceImplSpec extends Specification{
def "First spock test I ever wrote"(){
given: "some mock objects"
//1. define mock HttpURLConnection object
def mockConnObj=Mock(HttpURLConnection.class)
//2. defn of another mock
def mockURLAdaptor=Stub(URLAdapter)
mockURLAdaptor.openConnection(_)>>mockConnObj
when: "define some calls"
def test=new WeatherServiceImpl(mockURLAdaptor)
test.run("Raleigh")
then: "make some assertions"
1*mockConnObj.getResponseCode()
}//end def test
}
You can do it with Mocks as well, I just tried this and it worked
def "First spock test I ever wrote"() {
given: "some mock objects"
//1. define mock HttpURLConnection object
def mockConnObj = Mock(HttpURLConnection.class)
//2. defn of another mock
def mockURLAdaptor = Mock(URLAdapter)
1 * mockURLAdaptor.openConnection(_) >> mockConnObj
when: "define some calls"
def test = new WeatherServiceImpl(mockURLAdaptor)
test.run("Raleigh")
then: "make some assertions"
1 * mockConnObj.getResponseCode()
}//end def test
Hope it helps!
The following work(s):
#Subject([WeatherServiceImpl.class,URLConnection.class])
class WeatherServiceImplSpec extends Specification{
def "First spock test I ever wrote"(){
given: "some mock objects"
//1. define mock HttpURLConnection object
def mockConnObj=Mock(HttpURLConnection.class)
//=========================================
//2. defn of another mock
// Either/Or:
//def mockURLAdaptor=Mock(URLAdapter)
def mockURLAdaptor=Stub(URLAdapter)
//followed by:
mockURLAdaptor.openConnection(_)>>mockConnObj
//OR
def mockURLAdaptor=Stub(URLAdapter){
openConnection(_)>>mockConn
}
//BUT NOT:
def mockURLAdaptor=Mock(URLAdapter){
openConnection(_)>>mockConn
}
//=========================================
when: "define some calls"
def test=new WeatherServiceImpl(mockURLAdaptor)
test.run("Raleigh")
then: "make some assertions"
1*mockConnObj.getResponseCode()
}//end def test
}
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.
Wanna do the following:
BootStrap {
def init = {servletContext ->
........
MyDomainClass.metaClass.save = {->
delegate.extraSave()
//////// how to call original save() here?
}
}
.........
}
P.S. MyDomainClass#extraSave is defined as public void extraSave(){.....}
First of all, Bootstrap.groovy may not be the best place to do this kind of metaprogramming. The problem with this approach is that the changes to the classes will be applied when the application starts, but you may lose these changes when the application is reloaded. Obviously this is only an issue during development, and not an issue at all if you don't mind restarting the server every time you make a change, but I'll bet this would quickly become a major annoyance. In order to have the changes applied when app is reloaded as well, you should move the metaprogramming into a plugin, where you can hook into the onChange application lifecycle event.
So the steps are:
Create a plugin
Do the metaprogramming in the doWithDynamicMethods and onChange closures of the plugin descriptor
Here's a complete example where I "override" the chain() method on all the controller classes. The code to do likewise for the save() method of domain classes should only require some obvious replacements, e.g. use application.domainClasses instead of application.controllerClasses
def doWithDynamicMethods = {ctx ->
application.controllerClasses.each {controller ->
replaceChain(controller)
}
}
def onChange = {event ->
if (application.isArtefactOfType(ControllerArtefactHandler.TYPE, event.source)) {
def clz = application.getControllerClass(event.source?.name)
replaceChain(clz)
}
}
private replaceChain(controllerClass) {
// Save a reference to the grails chain() method
def grailsChain = controllerClass.metaClass.pickMethod("chain", [Map] as Class[])
controllerClass.metaClass.chain = {Map params ->
println "My code to execute before chain goes here"
// Invoke the grails chain() method
grailsChain.invoke(delegate, [params] as Object[])
println "My code to execute after chain goes here"
}
}
why not leveraging the GORM events for this purpose? In the Domain class:
def extraSave() {
// ...
}
def beforeInsert = {
extraSave()
}
def beforeUpdate = {
extraSave()
}
IMHO this a cleaner approach. Documentation can be found here
Not sure if the following works, but this might be a solution:
MyDomainClass.metaClass.origSave = MyDomainClass.metaClass.save
MyDomainClass.metaClass.save = {->
delegate.extraSave()
delegate.origSave()
}
Please give me feedbeck if the above worked...
In a Grails application I would like to add a foo() method to all my controller classes. I know that I can do this inside a plugin's doWithDynamicMethods closure using code like:
application.controllerClasses.toList()*.metaClass*.foo = { println 'foo called' }
However, I don't want to create a plugin just for this purpose. Is there anywhere else I can do this. I suspect it might be possible within the init closure of BootStrap.groovy, but I don't know how to get access to the GrailsApplication instance in this closure.
Thanks,
Don
def grailsApplication = org.codehaus.groovy.grails.commons.ApplicationHolder.application
The question was asked a long time ago, so my answer might not have been possible back then - but now it's April 2014. This code shows the most straightforward way of adding a method to all your controllers (from within BootStrap.init):
grailsApplication.controllerClasses.each { controllerClass ->
if (controllerClass.clazz.name.contains("org.idurkan.foo")) {
controllerClass.metaClass.respondError = { String message, int status = 404 ->
response.status = status
render([errorMessage: message] as JSON)
}
}
}
In BootStrap.groovy, inject grailsApplication like this: def grailsApplication
Make a call like mine above, substituting your own package name - to avoid messing around with plugins' classes.
Now in any controller you can use the repondError closure (invoke it like a method).
Note this does not add an action! It's just a utility closure/method available in every
controller.
class BootStrap {
def grailsApplication
def init = { servletContext ->
...
}
}