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.
Related
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).
I have the following JUnit test:
public class JavaTest {
final int value = 2;
#Test
#Repeat(times = value)
public void test() {
fail("Not yet implemented");
}
}
The #Repeat annotation comes from easytest-core, and the exact definition is here.
When I compile this as java source everything builds (and runs) fine. When I compile the exact same thing as groovy source, I get:
Groovy:Attribute 'times' should have type 'java.lang.Integer'; but found type 'java.lang.Object' in #org.easetech.easytest.annotation.Repeat GroovyTest.groovy
After searching the internets, I found a few similar discussions on SO and jira.codehaus, but those deal with String - GString problems, so the solutions do not work for me.
How can I fix this?
Updates:
java.version=1.7.0_76
groovy.version=2.3.7
Think you're bumping into the fact groovyc doesn't treat final variables as inline constants like javac does
I tried changing your int variable like this:
final Integer value = Integer.valueOf(2).intValue()
which prevents the variable from being treated as an inline constant. After that change I get a compile error from the #Repeat annotation:
Expected Integer.valueOf(2).intValue() to be an inline constant
It looks like there's some acknowledgement of the inconsistency here in a Groovy JIRA: https://issues.apache.org/jira/browse/GROOVY-1628
There's also some further discussion here in this SO thread:
Does it make sense to mark variable as final in groovy?
It doesn't look like you're going to be able to get groovy to match the Java behavior for this scenario.
class Parent {
final static String newLine = "*"
}
class Child extends Parent{
List body = [3, 4, 5]
String toString() {
def str = new StringBuilder()
body.each { str.append(it + newLine) }
str
}
}
def c = new Child()
println c
The above is one trivial sample to illustrate the problem. It couldn't be compiled using Groovy plugin on Eclipse. Remove either final or static in the field of super class solves the problem. However, I have no idea why it's the case.
http://groovy.codehaus.org/Groovy+Beans
In this link it mentions the rules for property and fields used in Groovy. I suppose the one applied should be the last one, i.e. meta class. Unfortunately, I still couldn't understand the behavior.
The behavior is reproduced consistently in all versions of Groovy. Maybe someone could report one bug to Groovy team. I have never done this before. It would be more efficient if someone experienced could do that.
This is most probably https://issues.apache.org/jira/browse/GROOVY-5776 which is more difficult to fix than it looks like
As blackdrag already pointed out: it's a bug.
But another workaround is to add the protected keyword:
protected final static String newLine = "*"
I'm trying to wrote a pex test, and I noticed that it always was feeding a false value as one of the params that I wanted. My test looked like this (simplified: there are/were more params, but otherwise no different):
[PexMethod]
public void TestCtor(bool value)
{
ArbitraryType myType = new ArbitraryType(value);
}
I wanted to test a scenario where I would have pex do the exploration, ensuring that value would be true. I made another test that looked like this:
[PexMethod]
public void TestCtor(bool value)
{
Contract.Requires(value == true);
ArbitraryType myType = new ArbitraryType(value);
}
But when I have Pex explore that, it still spits in false to value and the test it generates "passes". If I put a line after the requirement saying Contract.Assert(!value); It'll create another test and pass true for value to fail the assertion.
The question is, why isn't Pex satisfying the code contract?
I'm not sure what Pex is going to do with Contracts in the test methods, but I can't see it being a Good Thing :)
If you want Pex to do this, the correct thing to do is use PexAssume.IsTrue(value).
I'd like to re-implement a method of a Java class. For example, for "hi".length() to return 4. (How) Can I do that?
I know using SomeClass.metaClass I can get a reference to an existing method and define new (or overriding) method, but I can't seem to be able to do that for existing Java methods.
Using Groovy, you can replace any method (even those of final classes) with your own implementation. Method replacement in Groovy uses the meta-object protocol, not inheritance.
Here's the example you requested, i.e. how to make String.length() always return 4
// Redefine the method
String.metaClass.invokeMethod = { name, args ->
def metaMethod = delegate.metaClass.getMetaMethod(name, args)
def result = metaMethod.invoke(delegate, args)
name == 'length' ? 4 : result
}
// Test it
assert "i_do_not_have_4_chars".length() == 4
Seems like it could be possible by abusing String metaClass. But the attempt I've done so far in groovy console didn't led to the expected result :
def oldLength = String.metaClass.length
String.metaClass.length = { ->
return oldLength+10;
}
println "hi".length()
outputs the sad 2
I think you could take a look at Proxy MetaClass or Delegating metaClass.
If you did redefine it, it would only work in Groovy code. Groovy can't change the way Java code executes.
In Groovy, "hi".length() is roughly equivalent to this Java:
stringMetaClass.invokeMethod("hi","length");
Because Groovy doesn't actually call length directly, metaClass tricks work in Groovy code. But Java doesn't know about MetaClasses, so there is no way to make this work.
Although this question is very old I like to point out another way (at least for newer Groovy versions) .
The length() method in java.lang.String is implemented from java.lang.CharSequence interface. In order to reimplement the method using the String-metaClass you need to "override" the method in the metaClass of the interface first.
CharSequence.metaClass.length = { -> -1}
String.metaClass.length = { -> 4 }
assert "i_do_not_have_4_chars".length() == 4
The solution using String.metaClass.invokeMethod changes the behaviour of all String-methods and is problematic. For instance, simply invoking "asdf".size() leads to an exception on my setup.