Grails build-test-data Plugin - Can't Find TestDataConfig.groovy - unit-testing

I'm trying to utilize the build-test-data plugin in my Grails (v2.4.3) app to assist with test data creation for unit testing, but while running my unit tests the plugin cannot find TestDataConfig.groovy to load my specified values (for unique constraint tests, etc).
I've installed the plugin via including it in BuildConfig.groovy:
plugins {
...
test ":build-test-data:2.2.0"
...
}
I've ran the following command to create the TestDataConfig.groovy template, which places the file at \grails-app\conf\:
grails install-build-test-data-config-template
I've followed the general instructions on the plugin wiki to come up with a properly formatted file:
testDataConfig {
sampleData {
'<path>.User' {
def a = 1
username = { -> "username${a++}" }
}
}
}
(Where path is the fully-qualified class name.)
In my tests, I am using the following general format:
import grails.buildtestdata.TestDataConfigurationHolder
import grails.buildtestdata.mixin.Build
import grails.test.mixin.TestFor
import spock.lang.Specification
import spock.lang.Unroll
#TestFor(User)
#Build(User)
class UserSpec extends Specification {
def setup() {
mockForConstraintsTests(User)
TestDataConfigurationHolder.reset()
user = User.buildWithoutSave()
}
#Unroll
void "test #field must be unique"() {
given: 'a User exists'
user.save(flush: true)
when: 'another User is created with a non-unique field value'
def nonUniqueUser = User.buildWithoutSave()
nonUniqueUser."$field" = user."$field"
then: 'validation fails and the field has a unique constraint error'
!nonUniqueUser.validate()
nonUniqueUser.errors.errorCount == 1
nonUniqueUser.errors.getFieldError("$field").code == 'unique'
where:
field << ['username', '<some other field>']
}
}
But, when the test is run (using IntelliJ IDEA) TestDataConfig.groovy cannot be found via the following method in the plugin:
static Class getDefaultTestDataConfigClass() {
GroovyClassLoader classLoader = new GroovyClassLoader(TestDataConfigurationHolder.classLoader)
String testDataConfig = Holders.config?.grails?.buildtestdata?.testDataConfig ?: 'TestDataConfig'
try {
return classLoader.loadClass(testDataConfig)
} catch (ClassNotFoundException ignored) {
log.warn "${testDataConfig}.groovy not found, build-test-data plugin proceeding without config file"
return null
}
}
So the test continues on without a config file and I do not get uniquely generated data sets.
I've even tried explicitly including the file in Config.groovy:
grails.buildtestdata.testDataConfig = "TestDataConfig"
But, the same method in the plugin shows that Holders.config? is null.
I've looked at a few solutions to a similar problem here on StackOverflow with nothing working in my case; I cannot figure out how to get my app to detect the presence of the TestDataConfig.groovy file.
Any ideas? Thanks so much!

Related

Unit testing a non-class Groovy function using Gradle

I was given a Jenkins Library project, and I'd like to write some unit tests for it. I'm currently using the Jenkins Pipeline Shared Libraries Gradle Plugin which sets up source paths for unit and integration tests.
The source file I want to test is located at src/org/example/pipeline/Terraform.groovy and looks like:
package org.example.pipeline
def terraformNewWorkspace(String env, Map config) {
def tfWSCreate = """
#!/bin/bash
set -e
terraform workspace new ${env} || true
terraform workspace select ${env}
terraform workspace list
"""
if (config.currentWorkingDirectory != null) {
dir("${config.currentWorkingDirectory}") {
sh(returnStatus: false, script: tfWSCreate)
}
} else {
sh(returnStatus: false, script: tfWSCreate)
}
}
and I've created a test/unit/groovy/example/PipelineTests.groovy which looks like the following:
import static org.example.pipeline.Terraform.terraformNewWorkspace as terraformNewWorkspace
class TerraformUnitTests extends GroovyTestCase {
void testNewWorkspace() {
terraformNewWorkspace("dev", [currentWorkingDirectory: "/tmp/test"])
}
}
However I get a groovy.lang.MissingMethodException at PipelineTests.groovy when I try to execute this using gradle test. I've also tried the following as some documentation seems to indicate functions that are not part of a class get added in Groovy to a class with the name of the file, but I get the same exception about a missing method.
import org.example.pipeline.Terraform
...
t = new Terraform()
t.terraformNewWorkspace("dev", [currentWorkingDirectory: "/tmp/test"])
What is the correct way to import and test this individual function using gradle/groovy?
It was a simple syntax error. I forgot to use def for my variable:
import org.example.pipeline.Terraform
class TerraformUnitTests extends GroovyTestCase {
void testNewWorkspace() {
def t = new Terraform()
t.terraformNewWorkspace("dev", [currentWorkingDirectory: "/tmp/test"])
}
}

Grails Spock unit test requires to mock transaction manager

In Grails 3.1.12, I want to unit test a service:
#Transactional
class PlanService {
List<Plan> getPlans(Map params) {
def currentUser = (User)springSecurityService.getCurrentUser()
return Plan.findAllByCompany(currentUser.employer, params)
}
}
Like this:
#TestFor(PlanService)
#Mock([Plan, User, Company])
class PlanServiceSpec extends Specification {
void "Retrieve plan from the current user"() {
setup:
// create and save entities here
when: "the plans are retrieved"
def params = null
def plans = service.getPlans(params)
then: "the result should only include plans associated to the current user's company"
plans.size() == 2
}
Running the test from the console:
grails> test-app my.PlanServiceSpec -unit
Fails with:
my.FundingPlanServiceSpec > Retrieve plan from the current user FAILED
java.lang.IllegalStateException at PlanServiceSpec.groovy:48
and in the test report (HTML):
java.lang.IllegalStateException: No transactionManager was specified.
Using #Transactional or #Rollback requires a valid configured transaction manager.
If you are running in a unit test ensure the test has been properly configured
and that you run the test suite not an individual test method.
Now if I comment out the #Transactional annotation in the service, the test passes, but that's not the intended implementation. I am able to work around the problem by mocking the transaction manager:
service.transactionManager = Mock(PlatformTransactionManager) {
getTransaction(_) >> Mock(TransactionStatus)
}
But this seems very awkward, if not wrong.
Is there some incantation I forgot to invoke?
EDIT: looks similar to an old bug, but it's been closed more than a year.
Have you tried what a comments says that fixes the problem? If not, try to annotate the test class with:
#TestMixin(DomainClassUnitTestMixin)
and then:
service.transactionManager = getTransactionManager()
Was getting the same error in grails 3.3.2 when trying to test transactional service.
adding DataTest interface solved the issue for me.
class HelloServiceSpec extends Specification implements ServiceUnitTest<HelloService>, DataTest {
}

Unit test fails

I am new in Grails, I want to write unit tests for services using Spock. However I have the following issue.
import grails.transaction.Transactional
#Transactional
class BService {
boolean createB(){
return true
}
...
}
For this class I wrote the following test:
class BServiceTest extends Specification {
def "test createB"(){
given:
def service = new BService()
when:
def boolean temp
temp = service.createB()
then:
temp == true
}
}
The error I am getting when I run this test is the following:
java.lang.IllegalStateException: No transactionManager was specified. Using #Transactional or #Rollback requires a valid configured transaction manager. If you are running in a unit test ensure the test has been properly configured and that you run the test suite not an individual test method.
and it shows in GrailsTransactionTemplate.groovy:60
I would really appreciatie if anyone can give me a hint.
Add a #TestFor(BService) annotation to your unit test and use the instance of the service that it automatically provides. See http://grails.github.io/grails-doc/3.0.x/guide/testing.html for more information on testing.
Thank you ataylor for your reply. However I did a mistake, so I am now ansewring my own question.
First of all, the name conversion is wrong. When I create a service in grails there is automatically set a unit-test for this, so in that case I would have:
#TestFor(BService)
class BServiceSpec extends Specification {
def setup() {
User u = new User(1)
u.save()
}
def cleanup() {
}
def "test something"(){
}
}
In this case when I write a unit test, it runs.
The test that I had before was a functional test where I could not pass objects from the domain, so I had an error of the transactional manager.

Is it possible to mock a controller and have access to the FormTagLib in the same unit test?

As the title states, is it possible to have a unit test for a controller, and mock a tag lib?
As it stands, I have a User controller. Many of the actions use the
g.message(code: 'something.something')
call to set a message on the page.
#TestFor(UserController)
#Mock(User)
#TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification
{
UserService userServiceMock = Mock(UserService)
def setup()
{
controller.userService = userServiceMock
}
def cleanup()
{
}
void "test manageDevice"()
{
given:
def g = mockTagLib(FormTagLib)
when:
controller.manageDevice()
then:
model.pageTitle == 'message.device'
}
With that code I'm trying to hit the controller action but because of the g.message, it's failing with an error saying that it can't set a value to null. Pretty much because it doesn't see the "g.message"
I'm a little unsure if my unit test needs written differently, or if I'm just missing something.
Any help would be great!
EDIT:
Some updates using messageSource:
void "test manageDevice"()
{
given:
messageSource.addMessage 'user.devices', request.locale, 'Manage Devices'
when:
controller.manageDevices()
then:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
}
It seems to still be complaining because it doesn't have context of the "g" namespace on the controller. I'll note as well, I don't have context of 'addMessage' from messageSource. Not sure why, it should be there.
In the same controller, and many others, the only taglib we use in the controller scope is 'g' for the 'g.message' set in each action. The only other call that's being done, is one using the 'g' for a call like 'g.fixRedisIssue'
You can make a unit test for a controller with a mocked TagLib by including the necessary TagLib classes in the value of the grails.test.mixin.Mock annotation on the Spec class. For example, if you have the following TagLib:
(based on code in Grails documentation)
package org.grails.samples
class SimpleTagLib {
static namespace = 'g'
def hello = { attrs, body ->
out << "Hello ${attrs.name ?: 'World'}"
}
}
And you have the following controller:
package org.grails.samples
class SimpleController {
def flashHello() {
flash.message = g.hello()
}
}
You could test it with the following specification:
package org.grails.samples
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SimpleController)
#Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {
void 'test flashHello'() {
when:
controller.flashHello()
then:
flash.message == 'Hello World'
}
}
If you have multiple classes to mock, you can specify them in an array argument to #Mock. So you can add any necessary tag libraries to UserControllerSpec by changing #Mock(User) to something like #Mock([User, FormTagLib, YourFixRedisIssueTagLib]). However, I'd be surprised if you actually did need to add FormTagLib because in my Grails testing it seems to be included by default.
As some further advice, some of the code you posted doesn't make any sense. Specifically:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
Decide specifically what you want to test for and focus on writing the simplest code possible for that.
Also, based on your description of what's happening, I think that you're getting thrown off because your IDE is not properly integrated with Grails to see the g context.
Instead of returning the message text to the view from the controller, why not return the message code instead? For example, if you have something like this in your view:
<p>${flash.message}</p>
You can replace it with this:
<p><g:message code="${flash.code}" /></p>
And then set the code in the controller:
flash.code = "user.devices"
Then you'll be able to test the controller methods painlessly :)

Grails 2.1 Unit Testing Command Object mockForConstraintsTests not working?

I have used manually written as well as Grails generated Unit tests for this command object:
package myapp
#grails.validation.Validateable
class SearchCommand {
String basisBuild
String buildToSearch
static constraints = {
basisBuild(blank: false)
}
}
After having my hand written unit test fail I used Grails:
create-unit-test myapp.SearchCommand
I filled in the Unit Test, and made an assertion that should pass per documentation on mocked constraints:
package myapp
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
#TestMixin(GrailsUnitTestMixin)
class SearchCommandTests {
void setUp() {
mockForConstraintsTests(SearchCommand)
}
void tearDown() {
// Tear down logic here
}
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.validate(basisBuild: "")
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
}
Why am I getting this failure?
grails> test-app
| Running 9 unit tests... 9 of 9
| Failure: testSomething(com.siemens.soarian.sf.gap.SearchCommandTests)
| java.lang.AssertionError: expected:<blank> but was:<null>
at org.junit.Assert.fail(Assert.java:93)
I believe I found the grails supported way to unit test Command objects in grails 2.0. You need to use mockCommandObject provided by the ControllerUnitTestMixin.
Credit to Erik
http://www.jworks.nl/2012/04/12/testing-command-objects-in-grails-2-0/
EDIT
Using validate() appropriately and mockForConstraintsTest should work if the patch mentioned in the existing Grails bug is in place (Thanks to #codelark for bringing that up). In order to test the command object from a Web App standpoint (using controller) the below information would be helpful.
Test Command Object Using Controller action:-
A command object is only deemed as such when it is used as a parameter in one of the action method inside a controller. Refer Command Objects (Warning NOTE).
Use SearchCommand in an action method, you should be able to assertEquals.
Sample:
void testSomething() {
YourController controller = mockController(YourController) //Or instantiate
SearchCommand commandUnderTest = new SearchCommand ()
//Note the usage here. validate() does not take parameters
commandUnderTest.basisBuild = ''
commandUnderTest.validate()
//Call your action
controller.searchCommandAction(commandUnderTest)
assert response.text == 'Returned'
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
YourController's action:-
def searchCommandAction(SearchCommand sc){
render "Returned"
}
Note:
With out the patch from the grails bug we see the below error in #Grails 2.1.4, 2.2.0 & 2.2.1
I get an error when I only correct the validation and use mockForConstraintTests without using controller action:
You are using the validate method incorrectly. You never set the field on the class, so the field is null, not blank. Try changing your test as follows:
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.basisBuild = ""
assertFalse commandUnderTest.validate()
assertEquals 'blank', commandUnderTest.errors['basisBuild']
}
Edit: There is also a grails bug when testing command classes that use the #Validatable annotation. There are some workarounds in the bug commentary.