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)
Related
In karate version 0.9.5 I was able to use System.setProperty('message', message) during a mock invocation. Then that property was available inside a feature using karate.properties['message']. I have upgraded to version 1.0.1 and now result of karate.properties['message'] results in undefined
Spock Test code
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock()
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
System.setProperty('foo', 'bar')
when:
Results results = Runner.path("classpath:").tags("~#ignore").parallel(5)
then:
results != null
1 * messageLogger.logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty("message", message)
}
}
}
Controller
#RestController
public class MessageController {
#Autowired private MessageLogger messageLogger;
#GetMapping("/message")
public String message() {
String message = "Important Message";
messageLogger.logMessage(message);
return message;
}
}
MessageLogger
#Component
public class MessageLogger {
public void logMessage(String message) {
System.out.println(message);
}
}
karate-config.js
function fn() {
karate.configure('connectTimeout', 10000);
karate.configure('readTimeout', 10000);
karate.configure('ssl', true);
var config = {
localUrl: 'http://localhost:' + java.lang.System.getProperty('server.port'),
};
print('localUrl::::::::::', config.localUrl);
return config;
}
Feature
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* print 'message value ' + karate.properties['message']
0.9.5
2021-04-28 15:07:51.819 (...) [print] **foo value bar**
2021-04-28 15:07:51.826 (...) [print] **message value Important Message**
1.0.1
2021-04-28 14:36:58.566 (...) [print] **foo value bar**
2021-04-28 14:36:58.580 (...) [print] **message value undefined**
Link to project on github
I cloned your project and noticed a few outdated things (Groovy, Spock and GMaven+ versions). Upgrading them did not change the outcome, I can still reproduce your problem.
A also noticed that in your two branches the POM differs in more than just the Karate version number, also the dependencies differ. If I use the ones from the 1.0.1 branch, tests do not work under 0.9.5 anymore. So I forked your project and sent you two pull requests for each branch with a dependency setup working identically for both Karate versions. Now the branches really just differ in the Karate version number:
https://github.com/kriegaex/spock-karate-example/compare/karate-0.9.5...kriegaex:karate-1.0.1
BTW, for some reason I had to compile your code running JDK 11, JDK 16 did not work. GMaven+ complained about Java 16 groovy class files (bytecode version 60.0), even though GMaven+ should have used target level 11. No idea what this is about. Anyway, on Java 11 I can reproduce your problem. As the Spock version is identical for both branches, I guess the problem is within Karate itself. I recommend to open an issue there, linking to your GitHub project (after you have accepted my PRs). Spock definitely sets the system property, I have added more log output into the stubbing closure order to verify that. Maybe this is an issue concerning how and when Karate communicates with Spock.
Update: Peter Thomas suggested in his answer to store the value to be transferred to the feature in a Java object and access that one from the feature after the Spock test has set it. I guess, he means something like this:
https://github.com/kriegaex/spock-karate-example/commit/ca88e3da
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
MessageHolder.INSTANCE.message = message
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
Results results = Runner
.path("classpath:")
.systemProperty("foo", "bar")
.tags("~#ignore")
.parallel(5)
expect:
results
}
static class MessageHolder {
public static final MessageHolder INSTANCE = new MessageHolder()
private String message
private MessageHolder() {}
String getMessage() {
return message
}
void setMessage(String message) {
this.message = message
}
}
}
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage =
"""
function() {
var MessageHolder = Java.type('com.example.spock.karate.ApiTestRunnerSpec.MessageHolder');
return MessageHolder.INSTANCE.getMessage();
}
"""
* def message = call getMessage {}
* print 'message value ' + message
Update 2: This is the implementation of Peter's second idea to simply access Java system properties via JS. So I simplified the working, but unnecessarily complicated version with the message holder singleton, eliminating it again:
https://github.com/kriegaex/spock-karate-example/commit/e235dd71
Now it simply looks like this (similar to the original Spock specification, only refactored to be a bit less verbose):
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty('message', message)
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
expect:
Runner.path("classpath:").systemProperty("foo", "bar").tags("~#ignore").parallel(5)
}
}
The only important change is in the Karate feature:
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage = function() { return Java.type('java.lang.System').getProperty('message'); }
* print 'message value ' + getMessage()
The Runner "builder" has a .systemProperty() method which is recommended.
Please refer: https://github.com/intuit/karate/wiki/1.0-upgrade-guide#improved-test-suite-builder
So this should work. Else as I said in the comments, please submit a way to replicate.
Results results = Runner.path("classpath:")
.systemProperty("foo", "bar")
.tags("~#ignore").parallel(5)
EDIT: so I can confirm that karate.properties is made immutable at the time the test-suite starts.
So here are the 3 options:
change your test strategy so that all dynamic params are resolved before you start
instead of karate.properties[] do the old-school java.lang.System.getProperty('foo') call in Karate / JS, I'm pretty sure that will work
use a Java singleton to hold shared state for your test-runner and karate-feature
I added a service method call to Boostrap.groovy in my Grails 4.0.1 application:
#GrailsCompileStatic
class BootStrap {
GrailsApplication grailsApplication
SelfAssessmentRatingService selfAssessmentRatingService
def init = { servletContext ->
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
...
SelfAssessmentRating rating = SelfAssessmentRating.first()
if (!rating) {
selfAssessmentRatingService.createRatingsFromConfig()
}
}
def destroy = {
}
}
In the service method, an i18n message is fetched for each object I have specified in setup.selfAssessmentRatings in the application config.
#GrailsCompileStatic
#Slf4j
#Transactional
class SelfAssessmentRatingService implements GrailsConfigurationAware {
MessageSource messageSource
List<Map> setupAssessmentRatings
#Override
void setConfiguration(Config co) {
List selfAssessmentRatings = co.getProperty('setup.selfAssessmentRatings', List, null)
setupAssessmentRatings = selfAssessmentRatings as List<Map>
}
/**
* Saves a SelfAssessmentRating record for each one specified in the application config.
* This method gets called if there are no SelfAssessmentRating records saved.
*/
def createRatingsFromConfig() {
if (setupAssessmentRatings == null || setupAssessmentRatings.empty) {
log.info("Skipping creating SelfAssessmentRatings because none were specified " +
"in the config: 'setup.selfAssessmentRatings'")
return
}
log.info("Saving ${setupAssessmentRatings.size()} new SelfAssessmentRating records")
for (Map map in setupAssessmentRatings) {
// SelfAssessmentRating is a domain class
SelfAssessmentRating newRating = new SelfAssessmentRating()
newRating.rating = map.rating as Integer
newRating.englishText = map.englishText
newRating.translationKey = map.translationKey
newRating.save(failOnError: true)
// Verify we have that translation. This will throw an exception if the translation is not present.
messageSource.getMessage(newRating.translationKey, [].toArray(), Locale.default)
}
}
}
Here is the default config value for setup.selfAssessmentRatings; this is in the root of application.yml so it applies to all Grails environments:
setup.selfAssessmentRatings:
- {rating: 1, translationKey: "example.selfAssessment.noExperience", englishText: "No Experience or Knowledge"}
- {rating: 2, translationKey: "example.selfAssessment.someExperience", englishText: "Some Experience or Knowledge"}
- {rating: 3, translationKey: "example.selfAssessment.functionIndependently", englishText: "Able to function independently in this area"}
And I have those three messages defined in grails-app/i18n/messages.properties:
example.selfAssessment.noExperience=No Experience or Knowledge
example.selfAssessment.someExperience=Some Experience or Knowledge
example.selfAssessment.functionIndependently=Able to function independently in this area
My integration tests all pass in CircleCI (./gradlew -Dgrails.env=circleci integrationTest), but on my local machine I get the following error for each integration test:
org.springframework.context.NoSuchMessageException: No message found under code 'example.selfAssessment.noExperience' for locale 'en_US'.
at org.springframework.context.support.AbstractMessageSource.getMessage(AbstractMessageSource.java:161)
at com.hclabs.ojt.step.SelfAssessmentRatingService.$tt__createRatingsFromConfig(SelfAssessmentRatingService.groovy:46)
Does anyone know how I can get my integration tests to pass again locally ?
Is there an argument I could add or an environment variable to set when calling ./gradlew integrationTest on my local machine so that Grails can find the messages in grails-app/i18n/messages.properties?
Friends, hello.
I'am trying to use elements of object oriented approach in SOAP UI groovy scripting.
I read manual about "How to write a reusable script" here http://forum.loadui.org/viewtopic.php?f=1&t=15744
and I am trying to write a separate class with method which get data from database and set up address properties (Address).
My problem is that i receive the error message
groovy.lang.MissingPropertyException: No such property: sql1 for class
Utils error at line: 138
when run "main" Groovy script.
"Main" Groovy Script looks like:
// get a reference to the library TestSuite
library = testRunner.testCase.testSuite.project.testSuites["Library"]
// find the module within the library
module = library.testCases["module-name"].testSteps["Utils"]
// initialise the library; which places an instance of Example in the context
module.run(testRunner, context)
// get the instance of example from the context.
def utils = context.utils
// run the method, with parameter
log.info "utils.setAddress() = " + utils.setAddress("LivingPlace");
setAddress method is determined in class Utils which is looked like:
import groovy.sql.Sql;
class Utils
{
def log
def context
def testRunner
// Class constructor with same case as Class name
def Utils(logIn, contextIn, testRunnerIn)
{
this.log = logIn
this.context = contextIn
this.testRunner = testRunnerIn
}
def setAddress (addressType)
{
log.info testRunner;
log.info context;
log.info "Call the DB" ;
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver("com.microsoft.sqlserver.jdbc.SQLServerDriver");
sql1 = Sql.newInstance("jdbc:sqlserver://SRV1:1433; databaseName=DB;", "user", "123", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
sql1.eachRow('select top 1 '+
'Country '+
'from Address')
{ row ->
testRunner.testCase.testSteps['Address'].setPropertyValue("'country", "$row.Country");
}
return addressType //just for test
}
}
context.setProperty ("utils", new Utils(log, context, testRunner) )
log.info "Library Context:"+context;
If i delete everything related with sql it works fine. How to invoke the setAddress method without error described earlier and set Adress properties?
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).
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.