Spock: mocking/stubbing void method of final class - groovy

The mocked class here is org.apache.lucene.document.TextField. setStringValue is void.
My Specification looks like this...
given:
...
TextField textFieldMock = GroovyMock( TextField )
// textField is a field of the ConsoleHandler class, ch is a Spy of that class
ch.textField = textFieldMock
// same results with or without this line:
textFieldMock.setStringValue( _ ) >> null
// NB I explain about this line below:
textFieldMock.getClass() >> Object.class
the corresponding app code looks like this:
assert textField != null
singleLDoc.add(textField)
writerDocument.paragraphIterator.each{
println( "textField == null? ${ textField == null }" )
println( "textField ${ textField.getClass() }" )
textField.setStringValue( it.textContent ) // NB this is line 114
indexWriter.addDocument( singleLDoc )
The output from the printlns is
textField == null? false
textField class java.lang.Object
... which tends to prove that the mock is happening and getClass is being successfully replaced. If I get rid of the line textFieldMock.getClass() >> Object.class I get this output:
textField == null? false
textField null
In both cases the fail happens on the next line:
java.lang.NullPointerException
at org.apache.lucene.document.Field.setStringValue(Field.java:307)
at org.spockframework.mock.runtime.GroovyMockMetaClass.doInvokeMethod(GroovyMockMetaClass.java:86)
at org.spockframework.mock.runtime.GroovyMockMetaClass.invokeMethod(GroovyMockMetaClass.java:42)
at core.ConsoleHandler.parse_closure1(ConsoleHandler.groovy:114)
Line 114 is the setStringValue line. Field here is the (non-final) superclass of TextField.
To me it appears that something funny is happening: as though Spock were saying to itself: "ah, this class TextField is final, so I'll consult its parent class, and use the method setStringValue from there... and I find/decide that it is not a mock..."
Why is setStringValue not being mocked (or "substituted" or whatever the right term is for a method...)?
later
I went and took a look at Field.java in the package in question. The relevant lines are:
public void setStringValue(String value) {
if (!(fieldsData instanceof String)) {
throw new IllegalArgumentException("cannot change value type from " + fieldsData.getClass().getSimpleName() + " to String");
}
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
fieldsData = value;
}
... line 307 (implicated for the NPE) turns out to be the first throw new IllegalArgumentException... line. Quite odd. Suggesting that fieldsData is null (as you'd expect).
But why does Spock find itself processing this bit of code in class Field at all? Illogical: it's mocking, Jim, but not as we know it.
PS I later tried it with a (real) ConsoleHandler and got the same results. I have just noticed that when Spock output suggests you use a GroovyMock it says "If the code under test is written in Groovy, use a Groovy mock." This class isn't... but so far in my testing code I've used GroovyMock for several Java classes from Java packages, including others from Lucene... without this problem...
PPS workaround I got nowhere and in the end just created a wrapper class which encapsulates the offending final TextField (and will sprout whatever methods are needed...).
I have had experience of struggling with Lucene classes in the past: many of these turn out to be final or to have final methods. Before anyone makes the point that you don't need to test packages that can already be trusted (with which I agree!), you still need to test your own use of such classes as you develop your code.

I cannot really explain why it does not work for you as expected - BTW, stubbing getClass() is a bad idea and a bad example because it could have all kinds of side effects - but I do have a workaround for you: use a global mock.
The first feature method replicates your problematic test case, the second one shows how to solve it.
package de.scrum_master.stackoverflow
import org.apache.lucene.document.TextField
import spock.lang.Specification
class LuceneTest extends Specification {
def "Lucene text field normal GroovyMock"() {
given: "normal Groovy mock"
TextField textField = GroovyMock() {
stringValue() >> "abc"
}
when: "calling parent method"
textField.setStringValue("test")
then: "exception is thrown"
thrown NullPointerException
and: "parent method stubbing does not work"
textField.stringValue() == null
}
def "Lucene text field global GroovyMock"() {
given: "global Groovy mock"
TextField textField = GroovyMock(global: true) {
stringValue() >> "abc"
}
expect: "can call parent method"
textField.setStringValue("test")
and: "parent method stubbing works"
textField.stringValue() == "abc"
}
}

kriegaex provided the solution.
But, as said in my comment to his answer, I don't understand how it is that the TextField mock actually gets used by the concrete class instance ch. FWIW I put in the whole version of my (now passing) test.
def "parse should put several documents into the index"() {
given:
// NB the method we need to mock is setStringValue, which is void
// but (again to my astonishment) if the method is not mocked test still passes
TextField textFieldMock = GroovyMock(global: true) {
// setStringValue() >> "abc"
}
// turns out not to be needed... why not???
// ch.textField = textFieldMock
IndexWriter indexWriterMock = Mock( IndexWriter )
// commenting out this line means the test fails... as I'd expect
// i.e. because I'm "injecting" the mock to be used instead of ch's field
ch.indexWriter = indexWriterMock
// this line included to be able to mock static method loadDocument:
GroovyMock( TextDocument, global: true)
def textDocMock = Mock( TextDocument )
TextDocument.loadDocument(_) >> textDocMock
Paragraph paraMock1 = Mock( Paragraph )
paraMock1.textContent >> 'para 1'
Paragraph paraMock2 = Mock( Paragraph )
paraMock2.textContent >> 'para 2'
Paragraph paraMock3 = Mock( Paragraph )
paraMock3.textContent >> 'para 3'
textDocMock.getParagraphIterator() >> [paraMock1, paraMock2, paraMock3].listIterator()
Document lDocMock = GroovyMock( Document )
// commenting out this line means the test fails... as I'd expect
// i.e. because I'm "injecting" the mock to be used instead of ch's field
ch.singleLDoc = lDocMock
when:
def fileMock = Mock( File )
fileMock.path >> testFolder.root.path + '/dummy.odt'
fileMock.name >> '/dummy.odt'
ch.parse( fileMock )
then:
3 * indexWriterMock.addDocument( lDocMock )
// this is the crucial line which is now passing!
3 * textFieldMock.setStringValue(_)
PS worryingly, if I change either of these "3"s above here in the then clause to another value (e.g. 4) the test fails without outputting any test results. I just get this message from Gradle:
core.ConsoleHandlerUTs > Lucene text field global GroovyMock FAILED
org.spockframework.mock.TooFewInvocationsError at ConsoleHandlerUTs.groovy:255 ... FAILURE: Build failed with an
exception.
* What went wrong: Execution failed for task ':currentTestBunch'.
> java.lang.NullPointerException (no error message)
... where line 255 is the line ch.parse( fileMock ). This obviously doesn't happen with my wrapper class version... so for the moment I've gone back to that!
PPS I'm aware that I'm probably testing too many things at once here... but as a neophyte when it comes to TDD I often find one of the biggest puzzles is how to dig inside methods which do a few things with quite a few cooperating classes. The classes involved above prove somewhat inseparable (to me anyway).

Related

Is it possible to get the name of variable in Groovy?

I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]

Groovy DSL getting anonymous String assignments

I have a DSL that looks like this:
aMethod {
"a name"
"another name"
"and a third name"
}
My Problem is that I'm unable to access the three string, because calling the closure only returns the last statement. I tried to override the constructor of String(char[] value) which is called when an anonymous String-statement occurs:
def original
// primitive way to get the String(char[])-constructor
String.class.constructors.each {
if(it.toString() == "public java.lang.String(char[])") {
original = it
}
}
// overriding the constructor
String.metaClass.constructor = { char[] value ->
def instance = original.newInstance(value)
// ... do some further stuff with the instance ...
println "Created ${instance}"
instance
}
// teststring to call String(char[] value)
"teststring"
Unfortunately it didn't work and I thought anyway that it is quite complicated.
Thank you for the comments. Actually it would be great to define everything without quotes. But: After having a dsl that can be translated to java objects I'd loved to have additional annotations in my language at development time. I want to annotate duplicate names and so on. The IDE's I know better, Intellij and Eclipse handle Strings "a name" as one PSI-Elements. Splitting these elements can be very inconvinient ... I guess. I think statements in a closure like aMethod {a name} would result in an interpretation like aMethod {a.name}. That would mean that instead of having a StringLiteral Psi "a name", I would have an Object-Psi and a MethodCall-Psi or something like that. I don't know, and my next goal is just "parsing/creating" my java objects. Are you sure that it is impossible to override the String-Constructor?
Is any constructor called when you have a groovy script with this content:
"hello World"

Why `++` increments final field in Groovy?

Today, I came across rather peculiar issue/feature in Groovy.
It looks like it is possible to increment a final field using ++ operator in Groovy.
Does it look like a bug to you? This behaviour is not consistent with what I would expect from Java.
Does anyone have any idea how is it possible?
I have prepared a little Spock test to pin point the problem.
import spock.lang.Specification
class FinalModifierTest extends Specification {
def 'tests bizarre behaviour of final modifier in Groovy'() {
given:
Adder adder = new Adder()
expect:
adder.number.class == Integer
when:
adder.number = 7
then:
thrown(ReadOnlyPropertyException)
when:
adder.increment()
then:
adder.number == 2
}
}
class Adder {
final int number = 1
void increment() {
number++
}
}
Obviously, InteliJ informed me about final field assignment by showing below message:
'Cannot assign a value to a final field number', however the code still compiles and what is worse, it executes successfully!
I was running above example on:
JVM: 1.7.0_51
Groovy: 2.2.2
Does it look like a bug to you?
Yes. If you like you can file a JIRA at https://issues.apache.org/jira/projects/GROOVY and we can take a look.

Use groovy categories to add dynamic properties

Expanding on this blog post, I am trying to use a category to create a simple DSL for use with the javax.measure (JSR-275) classes (similar to TimeCategory for time intervals)
However, I do not want to add boilerplate code for each of the possible available methods (getMeter, getMilliMeter, getKelvin, getSecond etc.). I thought overriding the getProperty(String) method would work, but alas, it looks like the getProperty method defined in the category is not used when accessing the property directly.
Here is some simplified code to demonstrate:
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.measure.Measure;
#Category(Number)
class LengthCategory {
public Measure<BigDecimal, Length> getProperty(String unit){
return Measure.valueOf(this,Unit.valueOf(unit));
}
}
use(LengthCategory){
println 3.getProperty("m") // this works
println 3.m // this reports a non-exisiting property
prinlln 3.'m' // as does this
}
Assuming other methods of dynamically adding properties to a runtime object (e.g. Expando, subclassing GroovyInterceptible, mixins and other metaclass manipulations) is not viable and I would really rather not have to manually code getters for every possible unit and SI prefix combination. There are obviously other ways to go about creating a DSL for measurements, but I would still like to understand why this method would not work.
Could someone explain why the getProperty method of the category does not override .propertyName usage? I am obviously missing something important about the resolution of property names using the metaclass during runtime.
I don't know why getProperty doesn't work on categories. But you can define a get method on them that does basically the same (i think). This works:
#Category(Number)
class LengthCategory {
def get(String unit) {
"$this $unit"
}
}
use (LengthCategory) {
println 3.m // 3 m
println 3.'m' // 3 m
}
As far as I can tell, you can't actually extend Integers with full (i.e., readable and writable) properties using Category -- only with methods.
You can extend an Integer using read-only properties by using the method version of the property. You can even make it writable by including a set method. However, there doesn't seem to be a way to store the value passed in other than in a static variable and that ends up affecting all Integers.
Example:
$ cat catprop
#!/usr/local/bin/groovy
#Category(Integer)
class CatInteger {
private static String str = "default"
public static String setN(Integer i, String _str) { str = _str }
public static String getN(Integer i) { return str }
}
use (CatInteger) {
3.n = "333a"
println "3.n is " + 3.n
3.n = "333b"
println "3.n is " + 3.n
4.n = "444"
println "4.n is " + 4.n
println "3.n is " + 3.n
}
$ catprop
3.n is 333a
3.n is 333b
4.n is 444
3.n is 444
$
Note that in the last line 3.n return "444" because the stored field is static. I suppose that one could use a private HashMap and store a value for every Integer accessed, but that's too ugly to contemplate.
Another possibility would be to use the MetaClass Interface's getProperty() and setProperty(). However, I haven't looked into that so I don't know if it would work or not (just a thought).
Nice answer, but not sure, if you's still want to use JSR-275 now that JSR-363 is final?;-)

How to get groovy to evaluate ${sth} when stored in string?

I'm getting a text which contains ${somethingElse} inside, but it's just a normal String.
I've got a class:
class Whatever {
def somethingElse = 5
void action(String sth) {
def test = []
test.testing = sth
assert test.testing == 5
}
}
Is it possible with groovy?
EDIT:
my scenario is: load xml file, which contains nodes with values pointing to some other values in my application. So let's say I've got shell.setVariable("current", myClass). And now, in my xml I want to be able to put ${current.someField} as a value.
The trouble is, that the value from xml is a string and I can't evaluate it easily.
I can't predict how these "values" will be created by user, I just give them ability to use few classes.
I cannot convert it when the xml file is loaded, it has to be "on demand", since I use it in specific cases and I want them to be able to use values at that moment in time, and not when xml file is loaded.
Any tips?
One thing you could do is:
class Whatever {
def somethingElse = 5
void action( String sth ) {
def result = new groovy.text.GStringTemplateEngine().with {
createTemplate( sth ).make( this.properties ).toString()
}
assert result == "Number 5"
}
}
// Pass it a String
new Whatever().action( 'Number ${somethingElse}' )
At first, what we did, was used this format in xml:
normalText#codeToExecuteOrEvaluate#normalText
and used replace closure to regexp and groovyShell.evaluate() the code.
Insane. It took a lot of time and a lot of memory.
In the end we changed the format to the original one and crated scripts for each string we wanted to be able to evaluate:
Script script = shell.parse("\""+stringToParse+"\"")
where
stringToParse = "Hello world # ${System.currentTimeMillis()}!!"
Then we were able to call script.run() as many times as we wanted and everything performed well.
It actually still does.

Resources