java.lang.VerifyError: Bad access to protected data - groovy

I have the following Groovy file "test.groovy":
import groovy.transform.CompileStatic
#CompileStatic
class Test {
final Set<String> HISTORY = [] as HashSet
Set<String> getHistory() {
return HISTORY.clone() as HashSet<String>
}
}
Test test = new Test()
println test.history
Compiling it with Groovy 2.4.1 works fine, however, when I run "groovy test.class" I get the following error:
Caught: java.lang.VerifyError:
(class: Test, method: getHistory signature:()Ljava/util/Set;)
Bad access to protected data
java.lang.VerifyError:
(class: Test, method: getHistory
signature: ()Ljava/util/Set;)
Bad access to protected data
at test.run(test.groovy:12)
Any ideas what I am doing wrong here?

This actually is a bug in Groovy. A ticket was filed: https://issues.apache.org/jira/browse/GROOVY-7325
Workaround in this case:
It works if you are using a final HashSet<String> and then cast the clone up. Since the getter overrides the property basically anyway (make it private if you wanna be sure), it should not harm the intention of the original code.

Related

ModelMapper 2.4.4 and Groovy 3.0 compatibility issue

When switching from Groovy 2 to Groovy 3, ModelMapper 2.4.4 seems to now be failing to convert objects. ModelMapper itself does not throw an error, but rather just returns an object whose metaClass is still the initial class rather than the new post-conversion class.
This is demonstrated in the below code which, when run with Groovy 3 (tested with 3.0.2 and 3.0.9), then throws java.lang.IllegalArgumentException: object is not an instance of declaring class when accessing any of the properties of the returned object post-ModelMapping. This error does not happen when run in Groovy 2 (2.5.15).
Dependencies:
org.modelmapper:modelmapper:2.4.4
org.codehaus.groovy:groovy:3.0.9
import org.modelmapper.ModelMapper
class TestClass {
String fieldA
String fieldB
}
class TestClassDto {
String fieldA
String fieldB
}
TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
System.out.println(new ModelMapper().map(test, TestClass).fieldA)
The issue is in the fact that metaClass gets automatically mapped (so, testclass.metaClass gets replaces with TestClass.metaClass and groovy considers the final object to be an instance of TestClass). There are multiple ways to fix this.
You can explicitly set the metaclass after the mapping has been done:
def 'testMapping'() {
given:
TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
def mapped = new ModelMapper().map(test, TestClass)
mapped.metaClass = TestClass.metaClass
expect:
mapped.fieldA == 'anyA'
}
Alternatively, use #CompileStatic so that metaclass isn't generated at all.
Or you can even configure Modelmapper to skip metaclass:
mapper.typeMap(TestClassDto, TestClass)
.addMappings({ it.skip(GroovyObject::setMetaClass) } as ExpressionMap)

ClassCastException on attempt to mock Querydsl SQLQueryFactory

Trying to mock SQLQueryFactory of Querydsl for DAO unit testing. Using Mockito's deep stub for the very first time.
Below is the minimal code which fails
#Test
void tryMockQueryDsl() {
SQLQueryFactory sql = Mockito.mock(SQLQueryFactory.class, Mockito.RETURNS_DEEP_STUBS);
Mockito.when(sql.select(ArgumentMatchers.<Expression<?>>any())
.from(ArgumentMatchers.<Expression<?>>any())
.fetchFirst()
).thenReturn(null);
}
with the following exception:
java.lang.ClassCastException: class com.querydsl.sql.ProjectableSQLQuery$MockitoMock$1584151766 cannot be cast to class com.querydsl.sql.SQLQuery (com.querydsl.sql.ProjectableSQLQuery$MockitoMock$1584151766 and com.querydsl.sql.SQLQuery are in unnamed module of loader 'app')
What can be the problem?
Mocks can not be cast. Instead of RETURN_DEEP_STUBS mock each method on its own and return a mocked instance of the expected class.
If you do not want to suppress the warnings you will get for not defining the generic types, you can use the #Mock annotation instead to create the mocks, like described here.
This example doesn't make much sense for a testcase (as it tests nothing), but its a showcase on how to avoid the exception.
#RunWith(MockitoJUnitRunner.class)
public class Test {
#Test
public void tryMockQueryDsl() {
ProjectableSQLQuery projectableQuery = Mockito.mock(ProjectableSQLQuery.class);
// not really needed as this is the default behaviour
Mockito.when(projectableQuery.fetchFirst()).thenReturn(null);
SQLQuery query = Mockito.mock(SQLQuery.class);
Mockito.when(query.from(ArgumentMatchers.<Expression<?>>any())).thenReturn(projectableQuery);
SQLQueryFactory sql = Mockito.mock(SQLQueryFactory.class);
Mockito.when(sql.select(ArgumentMatchers.<Expression<?>>any())).thenReturn(query);
Expression expr = Mockito.mock(Expression.class);
sql.select(expr).from(expr).fetchFirst();
}
}

Parameter specified as non-null is null when using Mokito anyObject() on Kotlin function

My code as below, refering to the solution in https://stackoverflow.com/a/30308199/3286489
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.*
class SimpleClassTest {
private fun <T> anyObject(): T {
Mockito.anyObject<T>()
return uninitialized()
}
private fun <T> uninitialized(): T = null as T
lateinit var simpleObject: SimpleClass
#Mock lateinit var injectedObject: InjectedClass
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testSimpleFunction() {
simpleObject = SimpleClass(injectedObject)
verify(injectedObject).settingDependentObject(anyObject())
}
}
I still have the below error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method my.package.InjectedClass.settingDependentObject, parameter dependentObject
Did I miss anything?
UPDATED
Below is the code tested (simplest form and working)
class SimpleClass(val injectedClass: InjectedClass) {
fun simpleFunction() {
injectedClass.settingDependentObject(DependentClass(Response.Builder().build()))
}
}
open class DependentClass(response: Response) {
}
open class InjectedClass() {
lateinit var dependentObject: DependentClass
fun settingDependentObject(dependentObject: DependentClass) {
this.dependentObject = dependentObject
}
}
By default Kotlin classes and members are final. Mockito cannot mock final classes or methods.
Thus when you write:
verify(injectedObject).settingDependentObject(anyObject())
the real implementation is called which requires non null argument.
To fix that either open your class and method or, even better, change SimpleClass to accept an interface as its constructor argument and mock the interface instead.
There is a project specifically to help deal with Kotlin "closed by default" in unit testing with Mockito. For JUNIT, you can use the kotlin-testrunner which is an easy way to make any Kotlin test automatically open up classes for testing as they are loaded by the classloader. Usage is simple, just add one annotation of #RunWith(KotlinTestRunner::class), for example:
#RunWith(KotlinTestRunner::class)
class MyKotlinTestclass {
#Test
fun test() {
...
}
}
This is thoroughly covered in the article Never say final: mocking Kotlin classes in unit tests
This covers your use case in an automatic way by allowing all classes to be mocked that otherwise would not be allowed.
I ran into the same issue with Mockito when using RETURNS_DEEP_STUBS. It seems like nulls are still returned for nested objects, even when using the kotlin-allopen plugin.
Please check out and comment on this issue on Mockito if you're having the same problem.
You can use this function instead
inline fun <reified T : Any> any(): T = Mockito.any(T::class.java) ?: T::class.java.newInstance()

Not able to dynamically add method to Groovy using metaClass from within instance

I'm afraid I'm a beginner in Groovy so this might be very silly. (using groovy 2.3.4)
I have the following code:
class Test {
public void method() {
def methodName = "dynamicMethodName"
this.metaClass."$methodName" = {->}
this."${methodName}"()
}
}
new Test().method()
Running this will throw the following error:
Caught: groovy.lang.MissingPropertyException: No such property: dynamicMethodName for class: groovy.lang.MetaClassImpl
groovy.lang.MissingPropertyException: No such property: dynamicMethodName for class: groovy.lang.MetaClassImpl
at Test.method(what.groovy:5)
at Test$method.call(Unknown Source)
at what.run(what.groovy:10)
Can someone help me figure out what I am doing wrong? I also tried not being 'too dynamic' and altered the code like so:
class Test {
public void method() {
this.metaClass.dynamicMethodName = {->}
this.dynamicMethodName()
}
}
new Test().method()
But I still get the same error
UPDATE
It seems that if I add the method outside the class by using an instance reference instead of using this, it works.
Is there no way to dynamically add methods for an instance from within the instance itself?
It turns out, if the class extends the class Expando, it works.
Answer found here: https://stackoverflow.com/a/7079038/56242

Generic type arguments in derived classes

Groovy seems to be unable to compile a class where I've derived from a Generic base class and overloaded a method returning a generic array. The same example in Java appears to compile correctly, which is surprising as I expected Groovy to have source-level compatibility [ref].
This can be reproduced with the following adapter example. This example can also be executed online at the awesome Groovy Web Console.
class Person {}
​abstract class Base​Adapter<T> {
public T[] getAll() {
return getAllInternal();
}
protected abstract T[] getAllInternal()
}
class PersonAdapter extends BaseAdapter<Person> {
protected Person[] getAllInternal() {
return new Person[0];
}
}​
Compiling this produces the message:
Script1.groovy: 12: The return type of Person[] getAllInternal() in PersonAdapter is incompatible with [Ljava.lang.Object; getAllInternal() in BaseAdapter
. At [12:5] # line 12, column 5.
protected Person[] getAllInternal() {
^
1 error
Have I got the syntax incorrect? Or, is this a Groovy issue?
I expected Groovy to have source-level compatibility.
The source-level compatibility is maybe 90% but not perfect (nor is this a goal).
Have I got the syntax incorrect? Or, is this a Groovy issue?
Probably a bug. Please file a bug report.

Resources