I'd like to achieve the following goals using groovy:
Prepend (prefix) all println statements in groovy with the current time
I want the continue printing those println statements to print on standard out (console in my case).
I want to do it at one generic place, so that I don't have to individually modify all of these println statements.
Is there anything (like groovy aspect, log4j console appender etc.) that I could use to have all println statements prepend the time?
Metaprogramming. You could have PrintStream.metaClass.invokeMethod intercept the call to the println method and add the date to the passed in string.
Something like this, adapted from example code in Subramaniam's "Programming Groovy":
System.out.metaClass.invokeMethod = {String name, args ->
def validMethod = System.out.metaClass.getMetaMethod(name, args)
if (! validMethod)
{
return System.out.metaClass.invokeMissingMethod(delegate,name,args)
}
if ( validMethod.name == 'println')
{
args[0] = "${(new Date()).toString()} : ${args[0]}".toString()
}
validMethod.invoke(delegate,args)
}
Related
I was doing a Groovy tutorial online there and after playing around with the code I observed some behaviour that I can't understand.
First I created a Map object like this:
def devMap = [:]
devMap = ['name':'Frankie', 'framework':'Grails', 'language':'Groovy']
devMap.put('lastName','Hollywood')
Then I called eachWithIndex to print out the values like so:
devMap.eachWithIndex { println "$it.key: $it.value"}
Which printed this to the console:
name: Frankie
framework: Grails
language: Groovy
lastName: Hollywood
But when I printed to the console from the eachWithIndex method like this using the arrow operator:
devMap.eachWithIndex { it, i -> println "$i: $it" }
The following got printed to the console:
0: name=Frankie
1: framework=Grails
2: language=Groovy
3: lastName=Hollywood
So what I can't understand is why the indexes got printed with the second statement and why there are = signs but no : signs between the key-value pairs?
Thanks.
When you use the no-arg version of eachWithIndex, it is the current entry in the Map. That means that it.key and it.value return what you expect.
When you use the two-arg version of eachWithIndex, again, it is the current entry in the Map and i is the current index. You're printing i, the index, and then since you are only printing it, you are getting the result of it.toString(), which formats the map entry as "${it.key}=${it.value}"
Your second example is equivalent to:
devMap.eachWithIndex { it, index -> println "$index: ${it.toString()}" }
where this shows that the toString() implementation uses the = syntax:
devMap.each { println it.toString() }
Note that this is closer to your goal (as I interpret it):
devMap.eachWithIndex { it, index -> println "$index: ${it.key}: ${it.value}" }
I am trying to fail the test case if string values are not same.I have created a class and a method to compare the string values.
public class test1 {
public String onetwo(str1,str2)
{
def first = str1
def second=str2
if (first==second)
{
return "Strings are same"
}
else
{
testrunner.fail("String Values are not same")
}
}
public String fail(reason)
{
return "implementation of method1"+reason
}
}
def objone =new test1()
def result = objone.onetwo('Soapui','Soapui')
def result1 = objone.onetwo('Soapui','SoapuiPro')
while executing it i am getting below message for last line of above code
ERROR:groovy.lang.MissingPropertyException: No such property: testrunner for class: test1
Please suggest how to use testrunner.fail or any other way to fail the test case if strings are not same.
Thank You
It can't find SoapUI's testrunner because you're accessing it inside your own class. (Note that the error message is trying to find test1.testrunner, which of course does not exist.) If you accessed testrunner from the top level of the script (where you define your variables) it should work.
If you still want to have a reusable class/method, an easy fix would be to have it return a boolean or an error message, and then you call testrunner.fail if your method returns false/error. Something like this (although a boolean return might make for cleaner code):
public class test1 {
public String onetwo(str1,str2)
{
def first = str1
def second=str2
if (first==second)
{
return "Strings are same"
}
else
{
return "String Values are not same"
}
}
...
}
def objone =new test1()
def result = objone.onetwo('Soapui','Soapui')
def result1 = objone.onetwo('Soapui','SoapuiPro')
if (result != "Strings are same")
{
testrunner.fail(result)
}
...
This thread from another site also describes a much more complex solution of making reusable Groovy libraries for SoapUI.
If two strings are to be compared in groovy script test step, then it is not required to write a class, instead can be achieved with below statement.
Matching Samples - note that nothing happens on successful assertion
String compare and fail if not equal
assert 'Soapui' == 'Soapui', "Actual and expected does not match"
Another example - with boolen
def result = true;assert result, "Result is false"
Non-Matching example - test fails on failure
Strings not matching, and shows error message
assert 'Soapui' == 'SoapuiPro', "Actual and expected does not match"
Another example with non zero test
def number=0;assert number, "Number is zero"
If you need only example of the class and like to access testRunner object, then we need pass it either to the class or to the method where testRunner is needed. Otherwise, other class do not know about the objects that are available to groovy script.
Here is some more information on the objects availability at various level of test case hierarchy.
When soapUI is started, it initializes certain variables and are available at Project, Suite, Test case, Setup Script, Teardown script etc., If you open the script editor you would see the objects available there.
For instance, groovy script test step has log, context, testRunner, testCase objects. However, if some one creates a class with in Groovy Script test step, those objects are not available with in that user defined class.
I have a groovy script that asks some questions from the user via a java.io.console object using the readline method. In addition, I use it to ask for a password (for setting up HTTPS). How might I use Spock to Unit Test this code? Currently it complains that the object is a Java final object and can not be tested. Obviously, I'm not the first one trying this, so thought I would ask.
A sketch of the code would look something like:
class MyClass {
def cons
MyClass() {
cons = System.console()
}
def getInput = { prompt, defValue ->
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
def inputTest = input?.toLowerCase()
input
}
}
I would like Unit Tests to test that some mock response can be returned and that the default value can be returned. Note: this is simplified so I can figure out how to do the Unit Tests, there is more code in the getInput method that needs to be tested too, but once I clear this hurdle that should be no problem.
EDITED PER ANSWER BY akhikhl
Following the suggestion, I made a simple interface:
interface TestConsole {
String readLine(String fmt, Object ... args)
String readLine()
char[] readPassword(String fmt, Object ... args)
char[] readPassword()
}
Then I tried a test like this:
def "Verify get input method mocking works"() {
def consoleMock = GroovyMock(TestConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
inputMethods = new MyClass()
inputMethods.cons = consoleMock
when:
def testResult = inputMethods.getInput('testPrompt', 'testDefaultValue')
then:
testResult == 'validResponse'
}
I opted to not alter the constructor as I don't like having to alter my actual code just to test it. Fortunately, Groovy let me define the console with just a 'def' so what I did worked fine.
The problem is that the above does not work!!! I can't resist - this is NOT LOGICAL! Spock gets 'Lost' in GroovyMockMetaClass somewhere. If I change one line in the code and one line in the test it works.
Code change:
From:
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
To: (add the null param)
def input = (cons.readLine(prompt, null).trim()?:defValue)?.toString()
Test change:
From:
1 * consoleMock.readLine(_) >> 'validResponse'
To: (again, add a null param)
1 * consoleMock.readLine(_, null) >> 'validResponse'
Then the test finally works. Is this a bug in Spock or am I just out in left field? I don't mind needing to do whatever might be required in the test harness, but having to modify the code to make this work is really, really bad.
You are right: since Console class is final, it could not be extended. So, the solution should go in another direction:
Create new class MockConsole, not inherited from Console, but having the same methods.
Change the constructor of MyClass this way:
MyClass(cons = null) {
this.cons = cons ?: System.console()
}
Instantiate MockConsole in spock test and pass it to MyClass constructor.
update-201312272156
I played with spock a little bit. The problem with mocking "readLine(String fmt, Object ... args)" seems to be specific to varargs (or to last arg being a list, which is the same to groovy). I managed to reduce a problem to the following scenario:
Define an interface:
interface IConsole {
String readLine(String fmt, Object ... args)
}
Define test:
class TestInputMethods extends Specification {
def 'test console input'() {
setup:
def consoleMock = GroovyMock(IConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
when:
// here we get exception "wrong number of arguments":
def testResult = consoleMock.readLine('testPrompt')
then:
testResult == 'validResponse'
}
}
this variant of test fails with exception "wrong number of arguments". Particularly, spock thinks that readLine accepts 2 arguments and ignores the fact that second argument is vararg. Proof: if we remove "Object ... args" from IConsole.readLine, the test completes successfully.
Here is Workaround for this (hopefully temporary) problem: change the call to readLine to:
def testResult = consoleMock.readLine('testPrompt', [] as Object[])
then test completes successfully.
I also tried the same code against spock 1.0-groovy-2.0-SNAPSHOT - the problem is the same.
update-201312280030
The problem with varargs is solved! Many thanks to #charlesg, who answered my related question at: Spock: mock a method with varargs
The solution is the following: replace GroovyMock with Mock, then varargs are properly interpreted.
I wonder how is this possible in groovy to start an array from the n element.
Look at the snippet :
static void main(args){
if (args.length < 2){
println "Not enough parameters"
return;
}
def tools = new BoTools(args[0])
def action = args[1]
tools."$action"(*args)
System.exit(1)
}
As you see am doing here a dynamic method invocation. The first 2 arguments are taken as some config and method name , the others I would like to use as method paramerts.
So how can I do something like this :
tools."$action"(*(args+2))
Edited : If not possilbe in native groovy Java syntax will do it :
def newArgs = Arrays.copyOfRange(args,2,args.length);
tools."$action"(*newArgs)
To remove items from the beginning of the args you can use the drop() method. The original args list is not changed:
tools."$action"(*args.drop(2))
Other option, like you are trying is to access from N element:
tools."$action"(*args[2..-1])
I'm just getting started with Groovy/Grails
I noticed the error messages you get when you validate a form look like this:
Property [{0}] of class [{1}] cannot be blank
For example this code to dump the errors to the console
s.errors.allErrors.each
{
println it.defaultMessage
}
Now, it.arguments contains the arguments that need to be filled in here.
The problem is, I can't find any method in the Grails or Groovy documentation that formats strings based on positional parameters like {0}, {1} and substitutes values from an array
I need something like python's %
What is the proper way to format these error strings so the parameters get substituted properly?
These markers are actually replaced using the standard java.text.MessageFormat APIs. If you display the messages using Grail's g:message tag, it will fill in the gaps if you pass a suitable args="..." attribute:
<g:message code="mymessagecode" args="${['size', 'org.example.Something']}"/>
Under certain circumstances (within GSP pages and from controllers IIRC) you cann call the tag like a function:
g.message(code:'mymessagecode',args: ['size', 'org.example.Something'])
Note, that the value to supply as message code is only a symbolic string constant. The actual translation (the message text with the "gaps" in it) will be read by the framework using Spring's reloadable resource bundles.
If all you actually have is a translation text, you can call the message formatting APIs directly. See for example:
import java.text.MessageFormat
...
args = ["english"].toArray()
println(MessageFormat.format("Translation into {0}", args))
// Or - as the method is variadic:
println(MessageFormat.format("Translation into {0}", "english"))
Look what Groovy can do for you, using a little bit of meta-programming.
MessagesBundle_en_US.properties:
greetings = Hello {0}.
inquiry = {0}: How are you {1}?
farewell = Goodbye.
ResourceBundleWithSugar.groovy:
import java.text.MessageFormat
class ResourceBundleUtils {
def propertyMissing(String name) { this.getString(name) }
def methodMissing(String name, args) {
MessageFormat.format(this.getString(name), args)
}
}
ResourceBundle.metaClass.mixin ResourceBundleUtils
def msg = ResourceBundle.getBundle("MessagesBundle", new Locale("en","US"));
println msg.greetings("Serge")
println msg.inquiry("Serge","Mary")
println msg.farewell // You can use also: msg.['farewell'] msg."farewell" or msg.getString("farewell")
Output:
Hello Serge.
Serge: How are you Mary?
Goodbye.