Using mockFor to mock Sql.call() in a groovy unit test - unit-testing

I'm trying to mock newInstance() and call() in groovy.sql.Sql:
package com.sample
import grails.test.GrailsUnitTestCase
import groovy.mock.interceptor.MockFor
import groovy.sql.Sql
class MySampleTests extends GrailsUnitTestCase {
void testThat_SqlCall_IsInvokedWithexpectedQuery() {
def mockSql = new MockFor(Sql.class)
mockSql.demand.newInstance { def datasource->
return mockSql
}
mockSql.demand.call { def sql ->
return 0
}
mockSql.use {
MySample targetObject = new MySample()
targetObject.myMethod()
}
}
}
Where this is the target code:
package com.sample
import groovy.sql.Sql
class MySample {
def dataSource
def myMethod() {
def conn = Sql.newInstance(dataSource)
conn.call("test")
}
}
It errs out with:
groovy.lang.MissingMethodException: No signature of method: groovy.mock.interceptor.MockFor.call() is applicable for argument types: (java.lang.String) values: [test]
The error makes it seem like the call() method is not being mocked. Is that the case? What's a fix for it?

Replacing mockSql.demand.call with mockSql.metaClass.call provides a mocked method, but requires manual verification that the method is called, and of the parameter values:
package com.sample
import grails.test.GrailsUnitTestCase
import groovy.mock.interceptor.MockFor
import groovy.sql.Sql
class MySampleTests extends GrailsUnitTestCase {
void testThat_SqlCall_IsInvokedWithexpectedQuery() {
def mockSql = new MockFor(Sql.class)
def callInvoked = 0
mockSql.demand.newInstance { def datasource->
return mockSql
}
mockSql.metaClass.call { def sql ->
assert sql == "test"
++callInvoked
return 0
}
mockSql.use {
MySample targetObject = new MySample()
targetObject.myMethod()
}
assert callInvoked == 1
}
}
I don't know enough groovy yet to understand why this is the case, but it solved the issue for me.
Additional cosmetic changes plus moving the targetObject instantiation out of mySql.use {} result in:
package com.sample
import grails.test.GrailsUnitTestCase
import groovy.mock.interceptor.MockFor
import groovy.sql.Sql
class MySampleTests extends GrailsUnitTestCase {
MySample targetObject = new MySample()
void testThat_SqlCall_IsInvokedWithexpectedQuery() {
def mockSql = new MockFor(Sql)
def callInvoked = 0
mockSql.demand.newInstance { datasource->
mockSql
}
mockSql.metaClass.call { sql ->
assert sql == "test"
++callInvoked
0
}
mockSql.use {
targetObject.myMethod()
}
assert callInvoked == 1
}
}

Related

Spock does not detect method invocation

Spock does not detect doTip method invocation
(I need shared for some "where" blocks.)
Used latest groovy and spock.
Why this code is wrong?
How fix it?
import spock.lang.Shared
import spock.lang.Specification
class Test extends Specification {
def controller
#Shared
String g = ""
#Shared
def tip = Mock(Tip)
def "test"() {
controller = new TController(tip: tip)
when:
controller.transform(g)
then:
1 * tip.doTip(_)
}
}
class Tip {
def doTip(String f) {}
}
class TController {
Tip tip
def transform(String g) {
tip.doTip(g)
}
}
Use setup() to create the mock as shown below:
#Grab(group='org.spockframework', module='spock-core', version='1.0-groovy-2.4')
import spock.lang.*
class Test extends Specification {
def controller
#Shared String g = ""
#Shared tip
def setup() {
tip = Mock(Tip)
}
def "test"() {
given:
controller = new TController(tip: tip)
when:
controller.transform(g)
then:
1 * tip.doTip(_)
}
}
class Tip {
def doTip(String f) {}
}
class TController {
Tip tip
def transform(String g) {
tip.doTip(g)
}
}
Result
JUnit 4 Runner, Tests: 1, Failures: 0, Time: 78

How to write unit test for this controller method?

I trying to learn grails unit testing, I having a method like
def getProductList(){
List<Product> products = productService.getSodaProductList();
render(view:"productList",model:[products:products])
}
I want to write test function to this using GrailsUnitTestCase
I tried like this
void testGetSodaProductList(){
def sodaProduct = new SodaProduct(productName:"Test Product",productDesc: "",price:10.0);
mockDomain(SodaProduct,[sodaProduct])
def controller = new SodaProductController();
mockController(controller)
def list = controller.getSodaProductList();
assertEquals(2,list.model.products.size())
}
But not working, can some suggest how to write this test function?
I generally use the following way for unit testing
Using spock:
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SodaProductController)
#Mock([SodaProduct, ProductService])
class SodaProductControllerSpec extends Specification {
void testGetSodaProductList() {
when:
SodaProduct sodaProduct =
new SodaProduct(productName: "Test Product",
productDesc: "Desc", price: 10.0)
controller.getProductList()
then:
view == "/sodaProduct/productList"
model.products.size() == 2
}
}
Without spock:
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
#TestFor(SodaProductController)
#Mock([SodaProduct, ProductService])
class SodaProductControllerTests {
void testGetSodaProductList() {
SodaProduct sodaProduct =
new SodaProduct(productName: "Test Product",
productDesc: "Desc", price: 10.0)
controller.getProductList()
assert view == "/sodaProduct/productList"
assert model.products.size() == 2
}
}

Groovy, how to mock interface keeping other methods

How to mock an object that implements an interface and methods not belonging to the latter? I need aplicacaoPT to implement BindingProvider and buscaAplicacaoPorCodigo. The cast works but the buscaAplicacaoPorCodigo call gives a MissingMethodException. I tried with a map and an Expando:
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
import javax.xml.ws.BindingProvider
#TestMixin(GrailsUnitTestMixin)
class CatalogClientServiceTests {
void testFetch() {
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
[ getRequestContext: { new HashMap<String, Object>() },
buscaAplicacaoPorCodigo: { String appCode -> "carrapato!" } ] as BindingProvider
}
def applicationDataFromCatalog = service.fetch("dino")
}
void testFetchFailsWithInvalidApplicationCode() {
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
def aplicacaoSoapPort = new Expando()
aplicacaoSoapPort.getRequestContext = { new HashMap<String, Object>() }
aplicacaoSoapPort.buscaAplicacaoPorCodigo = { String appCode -> "castanha!" }
aplicacaoSoapPort as BindingProvider
}
shouldFail(IllegalArgumentException) { service.fetch("dino") }
}
}
import javax.xml.ws.BindingProvider
class CatalogClientService {
def aplicacaoPT = new AplicacaoSoapService().getAplicacaoSoapPort()
def grailsApplication
def fetch(String appCode) {
Map<String, Object> req_ctx = ((BindingProvider)aplicacaoPT).getRequestContext();
req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, grailsApplication.config.catalogAppEndPointAddress);
def response = aplicacaoPT.buscaAplicacaoPorCodigo(appCode)
println "response: $response"
}
}
I also tried adding buscaAplicacaoPorCodigo to BindingProvider's metaclass but it sticks with one of the implementations for all tests. In the example below response will be printed (above) either "castanha!" or "carrapato!" on both tests for each run.
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
import javax.xml.ws.BindingProvider
#TestMixin(GrailsUnitTestMixin)
class CatalogClientServiceTests {
void testFetch() {
BindingProvider.metaClass.buscaAplicacaoPorCodigo = { String appCode -> "carrapato!" }
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
[ getRequestContext: { new HashMap<String, Object>() } ] as BindingProvider
}
def applicationDataFromCatalog = service.fetch("dino")
}
void testFetchFailsWithInvalidApplicationCode() {
BindingProvider.metaClass.buscaAplicacaoPorCodigo = { String appCode -> "castanha!" }
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
def aplicacaoSoapPort = new Expando()
aplicacaoSoapPort.getRequestContext = { new HashMap<String, Object>() }
aplicacaoSoapPort as BindingProvider
}
shouldFail(IllegalArgumentException) { service.fetch("dino") }
}
}
I stripped parts of the code above for the sake of simplicity.
I hope some of may help me! :)
I have zero experience with grails, but this seems a problem related with proxies. The resulting proxy doesn't have the buscaAplicacaoPorCodigo available.
AFAIK, you have two choices: either create a interface Buscador, with buscaAplicacaoPorCodigo, and create a super interface for both BindingProvider and Buscador, or, for a pure dynamic solution, metaClass the resulting proxied expando:
I mocked some of your classes, hope I got it right.
BindingProvider.groovy:
interface BindingProvider {
def getRequestContext()
}
And the rest:
class AplicacaoSoapService {
static BindingProvider getAplicacaoSoapPort() {
assert false, "Nope, must be mocked"
}
}
AplicacaoSoapService.metaClass.static.getAplicacaoSoapPort = {
def e = new Expando(
getRequestContext: { [:] }) as BindingProvider
e.getMetaClass().buscaAplicacaoPorCodigo = { String a -> "amendoa!" }
e
}
def provider = AplicacaoSoapService.aplicacaoSoapPort
assert provider.buscaAplicacaoPorCodigo("pudim") == "amendoa!"

Testing a custom Grails TagLib method that uses request object

Total 'testing newbie' wanting to test the addJsFile method of my custom TagLib. What am I missing?
TagLib:
import com.company.group.application.helper.Util
...
class MyTagLib {
static namespace = 'mytag'
def util
...
def addJsFile = {
if (util.isSecureRequest(request)) {
out << '<script src="https://domain.com/jsfile.js"></script>'
} else {
out << '<script src="http://domain.com/jsfile.js"></script>'
}
}
}
Test (as far as I could get):
import org.springframework.http.HttpRequest
import com.company.group.application.helper.Util
#TestFor(MyTagLib)
class MyTagLibTests {
def util
...
void testAddJsFileSecure() {
def mockUtil = mockFor(Util)
mockUtil.demand.isSecureRequest() { HttpRequest request -> true }
def jsCall = applyTemplate('<mytag:addJsFile />')
assertEquals('<script src="https://domain.com/jsfile.js"></script>', jsCall)
}
void testAddJsFileNotSecure() {
def mockUtil = mockFor(Util)
mockUtil.demand.isSecureRequest() { HttpRequest request -> false }
def jsCall = applyTemplate('<mytag:addJsFile/>')
assertEquals('<script src="http://domain.com/jsfile.js"></script>', jsCall)
}
}
Util isSecureRequest
boolean isSecureRequest(request) {
return [true or false]
}
Error
org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException: Error executing tag <mytag:addJsFile>: Cannot invoke method isSecureRequest() on null object
You need to set the mocked util in tagLib in order to use it.
void testAddJsFileSecure() {
def mockUtilControl = mockFor(Util)
mockUtilControl.demand.isSecureRequest() { HttpRequest request -> true }
//"tagLib" is the default bind object provided
//by the mock api when #TestFor is used
tagLib.util = mockUtilControl.createMock()
//Also note mockFor() returns a mock control
//which on createMock() gives the actual mocked object
def jsCall = applyTemplate('<mytag:addJsFile />')
assertEquals('<script src="https://domain.com/jsfile.js"></script>', jsCall)
//In the end of test you can also verify that the mocked object was called
mockUtilControl.verify()
}
You would not need def util in the test then.

How to mock a service in grails TagLib unit test

I have TagLib, Service and TestCase as follows
How to mock a service in a taglib to get expected result from service
TagLib:
class SampleTagLib {
static namespace = "sample"
def baseService
def writeName = { attrs, body ->
def result = baseService.findAttributeValue(attrs.something)
if(result)
out << body()
}
}
Service:
class BaseService {
def findAttributeValue(arg1) {
return false
}
}
TagLibUnitTestCase:
import spock.lang.*
import grails.plugin.spock.*
import org.junit.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import grails.test.mixin.Mock
#TestFor(SampleTagLib)
#Mock(BaseService)
class SampleTagLibSpec extends Specification {
def template
def setup(){
tagLib.baseService = Mock(BaseService)
}
def 'writeName'(){
given:
tagLib.baseService.findAttributeValue(_) >> true
def template ='<sample:writeName something='value'>output</sample:writeName>'
when: 'we render the template'
def output = applyTemplate(template, [sonething:'value')
then: 'output'
output =="output"
}
}
But it getting Error condition not satisfied. Getting output = " "
Expected output = "output"
You need to use the grails mockFor to mock out the service.
See Mocking Collaborators
Untested Example:
def strictControl = mockFor(BaseService)
strictControl.demand.findAttributeValue(1..1) { arg1 -> return true }
taglib.baseService = strictControl.createMock()