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!"
Related
I am using wso2 IS 5.10, for adding custom claim which needs to be added by fetching from db I am using custom adaptive function. But the below code is not working.
package org.wso2.custom.auth.functions;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser;
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.custom.auth.functions.internal.CustomAuthFuncComponent;
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.*;
public class SetForceAuthFunctionImpl implements SetForceAuthFunction {
private static final Log LOGGER = LogFactory.getLog(SetForceAuthFunctionImpl.class);
#Override
public JsAuthenticatedUser setForceAuth(JsAuthenticationContext context, boolean forceAuth) {
AuthenticatedUser lastAuthenticatedUser = context.getContext().getLastAuthenticatedUser();
LOGGER.info("lastAuthenticatedUser****:::::::::::"+lastAuthenticatedUser);
String userName = lastAuthenticatedUser.getUserName();
LOGGER.info("userName2****:::::::::::"+userName);
String tenantDomain = MultitenantUtils.getTenantDomain(userName);
String fullyQualifiedUserName=("USERS"+"/"+userName+"#"+tenantDomain);
Map<org.wso2.carbon.identity.application.common.model.ClaimMapping, String> claims = new HashMap<org.wso2.carbon.identity.application.common.model.ClaimMapping, String>();
claims.put(org.wso2.carbon.identity.application.common.model.ClaimMapping.build("test123", "test123", null, true), org.apache.commons.lang3.StringUtils.join("*******************",",,,"));
AuthenticatedUser authenticatedUserObj = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
authenticatedUserObj.setAuthenticatedSubjectIdentifier(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
authenticatedUserObj.setUserAttributes(claims);
authenticatedUserObj.setUserName(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
return new JsAuthenticatedUser(authenticatedUserObj);
}
}
package org.wso2.custom.auth.functions.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.wso2.carbon.identity.application.authentication.framework.JsFunctionRegistry;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.custom.auth.functions.GenerateHashFunction;
import org.wso2.custom.auth.functions.GenerateHashFunctionImpl;
import org.wso2.custom.auth.functions.GetClaimsForUsernameFunction;
import org.wso2.custom.auth.functions.GetClaimsForUsernameFunctionImpl;
import org.wso2.custom.auth.functions.GetUsernameFromContextFunction;
import org.wso2.custom.auth.functions.GetUsernameFromContextFunctionImpl;
import org.wso2.custom.auth.functions.SetForceAuthFunction;
import org.wso2.custom.auth.functions.SetForceAuthFunctionImpl;
#Component(
name = "custom.auth.functions.component",
immediate = true
)
public class CustomAuthFuncComponent {
private static final Log LOG = LogFactory.getLog(CustomAuthFuncComponent.class);
private static JsFunctionRegistry jsFunctionRegistry;
#Activate
protected void activate(ComponentContext ctxt) {
SetForceAuthFunction setForceAuthFunctionImpl = new SetForceAuthFunctionImpl();
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "setForceAuth",
setForceAuthFunctionImpl);
GetUsernameFromContextFunction getUsernameFromContextFunctionImpl = new GetUsernameFromContextFunctionImpl();
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getUsernameFromContext",
getUsernameFromContextFunctionImpl);
GetClaimsForUsernameFunction getClaimsForUsernameFunctionImpl = new GetClaimsForUsernameFunctionImpl();
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getClaimsForUsername",
getClaimsForUsernameFunctionImpl);
GenerateHashFunction generateHashFunctionImpl = new GenerateHashFunctionImpl();
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "generateHash",
generateHashFunctionImpl);
}
#Deactivate
protected void deactivate(ComponentContext ctxt) {
if (jsFunctionRegistry != null) {
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "setForceAuth");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getUsernameFromContext");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getClaimsForUsername");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "generateHash");
}
}
#Reference(
name = "user.realmservice.default",
service = RealmService.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRealmService"
)
protected void setRealmService(RealmService realmService) {
if (LOG.isDebugEnabled()) {
LOG.debug("RealmService is set in the custom conditional authentication user functions bundle");
}
CustomAuthFuncHolder.getInstance().setRealmService(realmService);
}
protected void unsetRealmService(RealmService realmService) {
if (LOG.isDebugEnabled()) {
LOG.debug("RealmService is unset in the custom conditional authentication user functions bundle");
}
CustomAuthFuncHolder.getInstance().setRealmService(null);
}
#Reference(
name = "registry.service",
service = RegistryService.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRegistryService"
)
protected void setRegistryService(RegistryService registryService) {
if (LOG.isDebugEnabled()) {
LOG.debug("RegistryService is set in the custom conditional authentication user functions bundle");
}
CustomAuthFuncHolder.getInstance().setRegistryService(registryService);
}
protected void unsetRegistryService(RegistryService registryService) {
if (LOG.isDebugEnabled()) {
LOG.debug("RegistryService is unset in the custom conditional authentication user functions bundle");
}
CustomAuthFuncHolder.getInstance().setRegistryService(null);
}
#Reference(
service = JsFunctionRegistry.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetJsFunctionRegistry"
)
public void setJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) {
this.jsFunctionRegistry = jsFunctionRegistry;
}
public void unsetJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) {
this.jsFunctionRegistry = null;
}
}
But when I am using setForceAuth(context, true); in adaptive authentication function to add custom claims its not working but working in custom authenticator.
Adaptive authentication script:
function onLoginRequest(context) {
doLogin(context);
}
function doLogin(context) {
executeStep(1,{
onSuccess: function (context) {
},
onFail: function(context){
executeStep(4,{
onSuccess: function (context) {
var subject = context.currentKnownSubject;
setForceAuth(context, true);
},
onFail: function(context){
}
});
}
});
}
The issue is that
setForceAuth(context, true);
executes the below code block
return new JsAuthenticatedUser(authenticatedUserObj);
There is no point in your code (authencation script or the java function) where the newly created JsAuthenticatedUser is set to the context.
What you need to do is to change the script like this
onSuccess: function (context) { context.currentKnownSubject = setForceAuth(context, true);}
or
the java function as below
`#Override
public JsAuthenticatedUser setForceAuth(JsAuthenticationContext context, boolean forceAuth) {
AuthenticatedUser lastAuthenticatedUser = context.getContext().getLastAuthenticatedUser();
LOGGER.info("lastAuthenticatedUser****:::::::::::"+lastAuthenticatedUser);
String userName = lastAuthenticatedUser.getUserName();
LOGGER.info("userName2****:::::::::::"+userName);
String tenantDomain = MultitenantUtils.getTenantDomain(userName);
String fullyQualifiedUserName=("USERS"+"/"+userName+"#"+tenantDomain);
Map<org.wso2.carbon.identity.application.common.model.ClaimMapping, String> claims = new HashMap<org.wso2.carbon.identity.application.common.model.ClaimMapping, String>();
claims.put(org.wso2.carbon.identity.application.common.model.ClaimMapping.build("test123", "test123", null, true), org.apache.commons.lang3.StringUtils.join("*******************",",,,"));
AuthenticatedUser authenticatedUserObj = AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
authenticatedUserObj.setAuthenticatedSubjectIdentifier(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
authenticatedUserObj.setUserAttributes(claims);
authenticatedUserObj.setUserName(MultitenantUtils.getTenantAwareUsername
(fullyQualifiedUserName));
context.getContext().setSubject(authenticatedUserObj);
}
Still it is not advisable to have a tenant aware user name on authentication scripts. Rather can you think of not using
setForceAuth()
function and use the provided user.localClaims[] instead?
I was trying to mock a private method's output which is being called inside another private method, I have no choice but to test the later private method, so I have added sample test code which I can represent here,
This Sample Class
package com.testableClass;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class TestableClass {
private int initialMockMethod(Object obj)
{
System.out.println(" ++++ Came Here ++++ ");
String str = getRestString("");
System.out.println("str ="+str);
return str.length();
}
private String getRestString(String abc)
{
String output="";
try {
URL url = new URL("https://gorest.co.in/public-api/users");//your url i.e fetch data from .
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP Error code : "
+ conn.getResponseCode());
}
InputStreamReader in = new InputStreamReader(conn.getInputStream());
BufferedReader br = new BufferedReader(in);
while ((output = br.readLine()) != null) {
System.out.println(output);
}
conn.disconnect();
return output;
} catch (Exception e) {
System.out.println("Exception in NetClientGet:- " + e);
}
return abc;
}
}
Now This PowerMock Class
package com.testableClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import junit.framework.Assert;
#RunWith(PowerMockRunner.class)
#PrepareForTest(fullyQualifiedNames = "com.testableClass.TestableClass")
public class PowerMockTest {
#Test
public void testPrivateMethod() throws Exception
{
String message = "Hello PowerMockito";
String expectation = "Expectation";
TestableClass mock = PowerMockito.spy(new TestableClass());
// PowerMockito.doReturn(expectation).when(mock, "getRestString", message);
PowerMockito.when(mock, "getRestString", message).thenReturn(expectation);
int count = Whitebox.invokeMethod(mock, "initialMockMethod", new Object());
System.out.println(" +++ Count : "+count+" ++++ ");
Assert.assertTrue(true);
}
}
my issue is when I am running my test case then
PowerMockito.when(mock, "getRestString", message).thenReturn(expectation);
executes original method and returns original output while my requirement is that,
when my test case is actually calling private method initialMockMethod it should not call getRestString instead of that it should return my mocked expected output which is "Expectation"
Instead of using reflection and PowerMock, I'd say that do not try to mock a private method. It is a hidden detail of the class.
What I suggest is that, if your method makes an HTTP request, then let it do that. But you can use a mock server to mock the response. But to do that, you need to make your endpoint external, so that it can be injected.
I changed your class a little bit; still the same purpose tho.
public class TestableClass {
private final String resourceUrl;
public TestableClass(String resourceUrl) {
this.resourceUrl = resourceUrl;
}
public int publicMethod() {
return initialMockMethod(null);
}
private int initialMockMethod(Object obj) {
var str = getRestString("");
return str.length();
}
private String getRestString(String abc) {
try {
var url = new URL(resourceUrl);
var conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP Error code : "
+ conn.getResponseCode());
}
var in = new InputStreamReader(conn.getInputStream());
var br = new BufferedReader(in);
var result = br.lines().collect(Collectors.joining("\n"));
conn.disconnect();
return result;
} catch (Exception e) {
System.out.println("Exception in NetClientGet:- " + e);
}
return abc;
}
}
and here's the test
public class TestingTestableClass {
#Test
#SneakyThrows
public void test() {
final var server = new MockWebServer();
server.start(9999);
var instance = new TestableClass("http://127.0.0.1:9999");
server.enqueue(
new MockResponse()
.setResponseCode(200)
.setBody("this is the response")
);
final int result = instance.publicMethod();
Assertions.assertEquals(
"this is the response".length(),
result
);
final var record = server.takeRequest();
final var method = record.getMethod();
Assertions.assertEquals("GET", method);
server.shutdown();
}
}
Check out MockWebServer here
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
}
}
I'm trying to test a method which throws different exceptions according to the messages in the exceptions it receives.
I'm having a hard time throwing a SOAPFaultException because its constructor expects a SOAPFault which is an interface.
So I need to mock an interface or a different approach. This project uses grails 2.2.4 and junit 4.10.
Any help is appreciated.
The code:
import javax.xml.ws.BindingProvider
import javax.xml.ws.soap.SOAPFaultException
class CatalogClientService {
AplicacaoPT aplicacaoPT = new AplicacaoSoapService().getAplicacaoSoapPort()
static transactional = false
def fetch(String codigoBuscado) {
try {
def response = aplicacaoPT.buscaAplicacaoPorCodigo(codigoBuscado)
def app = response[0].serviceData.aplicacao[0]
def appGeral = app.getGeral()
def allTags = app.palavras.palavraChave.findAll().collect { it }
def tags = allTags.unique()
[name: appGeral.nome, description: appGeral.descricao, tags: tags, admins: [appGeral.getLiderProjeto().getChave()]]
} catch (SOAPFaultException e) {
if (e.fault.faultString == "Provider Error") {
throw new IllegalArgumentException("Nenhuma aplicação encontrada com o código ${codigoBuscado}.", e)
}
throw new RuntimeException("Erro ao buscar aplicação de código ${codigoBuscado}.", e)
} catch (Exception e) {
throw new RuntimeException("Erro ao buscar aplicação de código ${codigoBuscado}.", e)
}
}
}
import grails.test.mixin.*
import grails.test.mixin.support.GrailsUnitTestMixin
import org.junit.*
import javax.xml.ws.soap.SOAPFaultException
import javax.xml.soap.SOAPFault
#TestFor(CatalogClientService)
class CatalogClientServiceTests {
final shouldFailWithCause = new GroovyTestCase().&shouldFailWithCause
void testFetchFailsWithInvalidApplicationCode() {
def fault = new Expando()
fault.metaClass.getFaultString = { -> "Provider Error" }
SOAPFaultException.metaClass.'static'.getFault = { -> fault }
AplicacaoPT.metaClass.buscaAplicacaoPorCodigo = { String id -> throw new SOAPFaultException() }
shouldFailWithCause(SOAPFaultException) { service.fetch("POPB") }
}
}
If all you want is a simple way to create an instance of SOAPFault you should be able to do something like this...
import javax.xml.ws.soap.SOAPFaultException
import javax.xml.soap.SOAPFault
// ...
new SOAPFaultException({} as SOAPFault)
If you want to provide some method implementations, like getFaultString() for example, you can do someting like this...
import javax.xml.ws.soap.SOAPFaultException
import javax.xml.soap.SOAPFault
// ...
def soapFault = [getFaultString: { 'some message'}] as SOAPFault
new SOAPFaultException(soapFault)
I hope that helps
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.