Grails custom taglib cannot use custom artefact during integration test - testing

I have written a plugin containing a custom TagLib which itself uses a custom artefact instances. The taglib works exactly as expected when the plugin is included in an application. However, I am unable to unable to write an integration test for it.
Let's say the custom artefact type is "Foo" and the artefact handler class is FooArtefactHandler
The (simplified) FooTagLib class looks like this:
class FooTagLib {
static namespace = "bar"
def eachFoo = { attrs, body ->
grailsApplication.fooClasses.each { foo ->
out << body()
}
}
}
The associated FooTagLibTests class looks like this:
import grails.test.mixin.*
#TestFor(FooTagLib)
class FooTagLibTests {
void testEachFoo() {
grailsApplication.registerArtefactHandler(new FooArtefactHandler())
// Classes AFoo and BFoo are in the test/integration folder
grailsApplication.addArtefact(FooArtefactHandler.TYPE, AFoo)
grailsApplication.addArtefact(FooArtefactHandler.TYPE, BFoo)
// just to check if artefacts are correctly loaded
assert grailsApplication.fooClasses.length == 2
assert applyTemplate("<bar:eachFoo>baz</bar:eachFoo>") == "bazbaz"
}
}
When I run this test, the result is the following:
| Failure: testeachFoo(com.my.package.FooTagLibTests)
| org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException: Error executing tag <bar:eachFoo>: No such property: fooClasses for class: org.codehaus.groovy.grails.commons.DefaultGrailsApplication
ThegrailsApplication in the taglib does not seem to be the same instance as the one in the test. Can someone explain this to me? Am I doing anything wrong here?

If this is an integration test you shouldn't use #TestFor, instead, extends GroovyPagesTestCase and declare grailsApplication:
class FooTagLibTests extends GroovyPagesTestCase {
def grailsApplication
void testEachFoo() {
grailsApplication.registerArtefactHandler(new FooArtefactHandler())
// Classes AFoo and BFoo are in the test/integration folder
grailsApplication.addArtefact(FooArtefactHandler.TYPE, AFoo)
grailsApplication.addArtefact(FooArtefactHandler.TYPE, BFoo)
// just to check if artefacts are correctly loaded
assert grailsApplication.fooClasses.length == 2
assert applyTemplate("<bar:eachFoo>baz</bar:eachFoo>") == "bazbaz"
}
}
That's because TestFor annotation will mock an instance of grailsApplication (used in unit tests).

Related

Getting error, Spring BeanCreationException: Error creating bean with name 'validateableConstraintsEvaluator'

I have updated my project from grails2.5.6 to grails3.3.9 and successfully completed unit testing.
While running the integration test I am getting the following Exception.
2019-03-07 18:26:12.169 WARN --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.spockframework.spring.SpringMockTestExecutionListener#157b7da6] to process 'before' execution of test method [public void com.mdw360.sec.UserControllerSpec.$spock_feature_0_1()] for test instance [com.mdw360.sec.UserControllerSpec#36f464d8]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'validateableConstraintsEvaluator': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException: Cannot get property 'config' on null object
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:185)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1646)
Controller
#Transactional(readOnly = true)
class UserController {
def userCache
def springSecurityService
def utilityService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def create() {
List<Role> roles = Role.list()
Map roleMap = [:]
roles.each { role -> roleMap[role] = false }
render view: 'create', model: [userCO: new SaveUserCO(), roleMap: roleMap]
}
......
......
}
TestClass
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
#Integration
#Rollback
class UserControllerSpec extends Specification {
#Autowired
UserController controller
}
def cleanup() {
}
void "test create render correct view and model when no role exists"() {
when:
controller.request.method='POST'
controller.create()
then:
assert controller.modelAndView.viewName == '/user/create'
assert controller.modelAndView.model.size() == 2
assert controller.modelAndView.model.roleMap.size() == 0
assert controller.modelAndView.model.userCO != null
}
void "test create render correct view and model when role exists"() {
when:
controller.request.method='POST'
createRole('ROLE_TEST')
controller.create()
then:
assert controller.modelAndView.viewName == '/user/create'
assert controller.modelAndView.model.size() == 2
assert controller.modelAndView.model.roleMap.size() == 1
assert controller.modelAndView.model.userCO != null
}
...
....
}
I am using Grails 3.3.9 with JDK 1.8, gormVersion 6.1.11 and
gradle3.5
I my case I have a folder called acceptance-test where I had some integration test I wanted to execute. I created a acceptanceTest gradle task similar to this one integration-test.gradle.
If the task acceptanceTest was executed, the BeanCreationException was thrown. I had to manually active the spring profile dev
./gradlew acceptanceTest -Dspring.profiles.active=test
The root problem is that some grails global variables (like grailsApplication, messageSource) are not created and are need to run test. To run integration test these global variables must be created or mocked by Grails. Setting spring.profile.active to test creates all these global variables.

How to create unit test for the function "beforeControllerAction" extended from yii framework

I need some idea, to create unit test for the action 'beforeControllerAction', which is extended from yii framework.
beforeControllerAction is parent method from any 'mycontroller' app controller, coming from framework core. You don't need to test specific core framework code (is already tested). You need to test your own code.
One way to test your controller is to extend/inherit your own 'mycontroller' controller first and build a test for it. Taken from this excellent article:
Create your unit test class under the protected/tests/unit
folder and name it the same as your class name you want to test,
adding a Test word after it.
In my case, I will create a file named ApiControllerTest.php that
contains all tests for ApiController.php class.
<?php
// You can use Yii import or PHP require_once to refer your original file
Yii::import('application.controllers.ApiController');
class ApiControllerTest extends ApiController
{
}
Open your ApiControllerTest.php unit test class in step #1
above and make it something similar like this (based on your
requirement and structure):
class ApiControllerTest extends CTestCase
{
public function setUp()
{
$this->api = new ApiController(rand());
}
public function tearDown()
{
unset($this->api);
}
}
Let’s try to test one single method in my ApiController.php, that is
formatResponseHeader. This is what it is doing.
public function formatResponseHeader($code)
{
if (!array_key_exists($code, $this->response_code))
{
$code = '400';
}
return 'HTTP/1.1 ' . $code . ' ' . $this->response_code[$code];
}
Now, to test this method, I’ll open ApiControllerTest.php and add this
code below after setUp() and before tearDown() methods:
public function testFormatResponseHeader()
{
$this->assertEquals('HTTP/1.1 400 Bad Request',$this->api->formatResponseHeader('400'));
$this->assertEquals('HTTP/1.1 200 OK',$this->api->formatResponseHeader('200'));
$this->assertEquals('HTTP/1.1 400 Bad Request',$this->api->formatResponseHeader('500'));
$this->assertNotEquals('HTTP/1.1 304 Not Modified',$this->api->formatResponseHeader('204'));
}
Save the change in ApiControllerTest.php and then try to run this in
protected/tests directory:
phpunit .

How to mock the 'new' operator

I'm testing some groovy code that uses a java library and I want to mock out the library calls because they use the network. So the code under test looks something like:
def verifyInformation(String information) {
def request = new OusideLibraryRequest().compose(information)
new OutsideLibraryClient().verify(request)
}
I tried using MockFor and StubFor but I get errors such as:
No signature of method: com.myproject.OutsideLibraryTests.MockFor() is applicable for argument types: (java.lang.Class) values: [class com.otherCompany.OusideLibraryRequest]
I'm using Grails 2.0.3.
I've just found that we can always overwrite a constructor via MetaClass, as Grails 2 will be reset MetaClass modification at the end of each test.
This trick is better than Groovy's MockFor. AFAIK, Groovy's MockFor does not allow us to mock JDK's classes, java.io.File, for example. However in the below example, you cannot use File file = new File("aaa") as the real object type is a Map, not a File. The example is a Spock specification.
def "test mock"() {
setup:
def fileControl = mockFor(File)
File.metaClass.constructor = { String name -> [name: name] }
def file = new File("aaaa")
expect:
file.name == "aaaa"
}
The second, optional parameter to MockFor's constructor is interceptConstruction. If you set this to true, you can mock the constructor. Example:
import groovy.mock.interceptor.MockFor
class SomeClass {
def prop
SomeClass() {
prop = "real"
}
}
def mock = new MockFor(SomeClass, true)
mock.demand.with {
SomeClass() { new Expando([prop: "fake"]) }
}
mock.use {
def mockedSomeClass = new SomeClass()
assert mockedSomeClass.prop == "fake"
}
Note, however, you can only mock out groovy objects like this. If you're stuck with a Java library, you can pull the construction of the Java object into a factory method and mock that.

Seems Like Groovy acts Differently on these two scenarios?

I have two domain classes like this, first namely Manager :
package com.mnm
class Manager {
String name;
static hasMany = [ project : Project, tasks : Tasks ]
static constraints = {
}
}
And second one namely, Project:
package com.mnm
class Project {
String projectTitle
String projectDescription
String description
static belongsTo = [ managers: Manager ]
static hasMany = [ tasks : Tasks ]
static constraints = {
}
}
And I wrote Integration test like this (to find the name of the projects via using Manager) :
void testCountProject() {
def manager = new Manager(name:'Anto').save()
manager.addToProject(new Project(projectTitle:'Grails'))
manager.addToProject(new Project(projectTitle:'Griffon'))
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
}
Well there is no error in it and the test passes! But when I add more stuffs into to the same test like (now I'm trying the reverse, finding the Manager name via using Project) :
void testCountProject() {
def manager = new Manager(name:'Anto').save()
def project1 = new Project(projectTitle:'Grails').save()
manager.addToProject(project1)
manager.addToProject(new Project(projectTitle:'Griffon'))
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
def noOfManager = Project.get(project.id)
def foundManager = noOfManager.managers.collect { it.name }
assertEquals(['Anto'],foundManager)
}
Now I get the error like this :
No signature of method: com.mnm.Manager.addToProject() is applicable for argument types: (null) values: [null] Possible solutions: addToProject(java.lang.Object), getProject()
Where I went wrong?
Thanks in advance.
You have the same problem in both cases, but the first isn't a proper test so it seems to work. The issue is that all properties are not-null by default, so your Project instances fail validation when you only set projectTitle.
In the first test you don't re-load the manager instance, you're still using the one in-memory because get() uses the Hibernate session as a 1st-level cache. If you flush and clear the session to force it to go to the database it will fail:
class MyTest extends GroovyTestCase {
def sessionFactory
void testCountProject() {
def manager = new Manager(name:'Anto')
manager.addToProject(new Project(projectTitle:'Grails'))
manager.addToProject(new Project(projectTitle:'Griffon'))
manager.save(flush: true)
sessionFactory.currentSession.clear()
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
}
}
The second one fails because you call save() on the Project instance and it returns null when validation fails. You don't need to save Project instances because they will be transitively saved when the containing Manager gets saved - the more standard pattern is the one you use in the first test.
You have a few options. One is to fix the validation errors :) Another is to check for validation errors. This requires a separate save() call so you have access to the not-null instance:
def project1 = new Project(projectTitle:'Grails')
project1.save()
if (project1.hasErrors()) {
// handle errors
}
else {
manager.addToProject(project1)
}
The third is failOnError which will throw an exception when validation fails:
def project1 = new Project(projectTitle:'Grails').save(failOnError: true)
manager.addToProject(project1)

testing grails iterative taglibs that use variables in body

I have a fair simple taglib like this (query is simplified for the sake of example):
def toptopics = { attrs, body ->
def topics = Topic.executeQuery("from Topic")
topics.each { topic ->
out << body(topic:topic)
}
}
Then I use it in the gsp as follows:
<g:toptopics>
<li>${topic.name}</li>
</g:toptopics>
Then I'm trying to write a test for it (code below):
void setUp() {
taglib = new MySampleTagLib()
}
void test_each_in_collection() {
assertEquals("tag 1;tag 2", taglib.toptopics() { "${topic.name};" })
}
But it keeps failing. The test output complain about "No such property: topic for class: MySampleTagLibTests
For some reason it's trying to evalute ${topic.name} before passing the string to the taglib. I've tried escaping the dollar sing with no success. Any tips on this?
To test a taglib, you should extend TagLibUnitTestCase. When you use this class you don't instantiate your tag class directly (as in the code above), but instead an instance is made available to you through the tagLib property. For example, let's assume I want to test the repeat tag of FooTagLib. This tag takes a single attribute and expects the tag to have a body that uses variables.
class FooTagLibTests extends TagLibUnitTestCase {
FooTagLibTests() {
// This line isn't necessary in this case because the test class is in the same package
// as the tag class and named ${TagLib}Tests
super(FooTagLib)
}
void testRepeat() {
def someText = 'blah'
// tagLib references an instance of FooTagLib. Invoke the bar tag passing it attribute
// values and a body
tagLib.repeat(times: '2') {
"body $someText "
}
// compare the expected and actual output
assertEquals 'body someText body someText ', tagLib.out.toString()
}
}