Trying to use JUnit 5 class Assertions, specifically assertAll() with Groovy.
Basic Java proves the following syntax:
#Test
public void someTest() {
assertAll('heading',
() -> assertTrue(firstName),
() -> assertTrue(lastName)
);
}
Since I'm stuck to Groovy v2.4, which does not support lambdas, what I do looks like (tried several options, however all of those provide the same error):
#Test
void 'Some test'() {
assertAll('heading',
{ -> assert firstName },
{ -> assert lastName }
)
}
Error message:
groovy.lang.MissingMethodException:
No signature of method: static org.junit.jupiter.api.Assertions.assertAll() is applicable for argument
types: (path.GeneralTests$_Some_test_closure10...)
values: [path.GeneralTests$_Some_test_losure10#1ef13eab, ...]
Looks like the problem is with assertAll() method itself, exception is quite obvious, however that's quite a challenge to get the possible solution, since I had to switch to Groovy from Python just recently.
Indeed, I am going to take some courses with Groovy in future, but you know, you always have to provide results for yesterday.
Would highly appreciate your suggestions!
In this particular case, you need to cast Groovy closure to org.junit.jupiter.api.function.Executable interface explicitly:
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.function.Executable
import static org.junit.jupiter.api.Assertions.*
class Test {
#Test
void 'Some test'() {
assertAll('heading',
{ assertTrue(true) } as Executable,
{ assertFalse(false) } as Executable)
}
}
Related
I stumbled across this when updating a large app from groovy 2 to 3 (and also to corresponding newer spock and geb versions).
This code behaves strange and also a different kind of strange in groovy 2 versus groovy 4.
I think we are running without "indy" here. I guess because all the transitive dependencies of our large app bring in specific groovy jars without indy. I should probably goe through them carefully and adapt our gradle build so that only "indy" versions of all jars are picked.
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output for groovy 2.5.15:
B.bar
A.beep
---
A.bar
A.beep
Output for groovy 4.0.0:
A.bar
A.beep
---
A.bar
A.beep
What I would have expected:
B.bar
B.beep
---
B.bar
B.beep
What's going on here? Bug or some strange, but expected corner case?
Where is the difference in behavior in between groovy 2 and 4 documented?
In our real app there was a difference already in between groovy 2 and 3 but I have been unable so far to create example code for that.
Is there a way to call the original method inside of invokeMethod? (Can't find anything in the docs, which are very sparse btw.)
I get your 3.0.9 output for Groovy 2.5.16, 3.0.10 and 4.0.1 -- indy enabled for all three.
Your implementation of invokeMethod relies on the behavior of ScriptBytecodeAdapter#invokeMethodOnSuperN which is what is behind super."$name"(*args). When handling "bar" message, the meta-method index has B.bar(java.lang.String) for "this" and B.super$2$bar(java.lang.String) for "super". super$2$bar is a meta-object protocol (MOP) method that provides the necessary INVOKESPECIAL instruction to reach A#bar(java.lang.String).
If you want the output of all calls to be from B then you can use this."$name"(*args) instead. In your specific case, there is no need to implement C as GroovyInterceptable and to try and route "foo", "bar" and "beep" yourself.
You can make your code produce the expected output by making the B class compiled statically:
import groovy.transform.CompileStatic
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
#CompileStatic
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output:
B.bar
B.beep
---
B.bar
B.beep
As it was mentioned by emilies in his answer, in the MOP use case scenario something like this happens:
c.bar('Hello')
invokeMethod('bar', ['Hello'] as Object[])
super."bar"(['Hello'] as Object[])
This super."bar"(['Hello'] as Object[]) is represented by B.super$2$bar(java.lang.String) method object which forces A.bar(java.lang.String) to be invoked right in the next call frame.
However, if you make the B class to be compiled statically, the method that is found to satisfy the super."bar"(['Hello'] as Object[]) expression, in that case, is B.bar(java.lang.String), and thus it gets invoked directly.
Regarding the differences between Groovy 2.5 and Groovy >=3.0, it looks like you have encountered a compiler bug. The bar('hello') inside the A.foo() method ignores the MOP and goes directly to this.bar(java.lang.String) which in this case is B.bar(java.lang.String).
It looks like it happens for the java.lang.String type (didn't check other types). However, when the type is java.util.Optional, then a call like beep(Optional.of('Hello')) inside the A.foo() method goes through the MOP and thus it discovers B.super$2$beep(java.util.Optional) method to be invoked:
I would like to execute a function (with parameters) through an annotation tag in a groovy script.
If we execute a method in our groovy script with this annotation it would print in the console (stderr) a custom message like:
warning: '<function_name>' is deprecated [[Use '<Deprecated.instead>' instead.][More info: '<Deprecated.more_info>']]
So, I have created a custom annotation like this
public #interface Deprecated {
public String instead() default null
public String more_info() default null
}
The goal is to use it like this:
def new_call() {
//new version of the method
}
#Deprecated(instead="new_call")
def call() {
//do something
}
In my example, it would output like this:
warning: 'call' is deprecated. Use 'new_call' instead.
I saw this post Groovy: How to call annotated methods, it's over 7 years old now but looks good so i'll look deeper.
I saw also Delegate.deprecated but i'm not sure if that's what i want
I'm not sure I am doing right. So if you have any advice or suggestions, I'll be happy to hear you.
Simple AOP Approach
This is very-very basic implementation with groovy out-of the box.
Deprecated Annotation
#Target([ElementType.METHOD])
#Retention(RetentionPolicy.RUNTIME)
#interface Deprecated {
String instead() default 'null'
String more_info() default 'null'
}
Class which should get this functionality
The class has to implement GroovyInterceptable - invokeMethod.
class SomeClass implements GroovyInterceptable {
#Override
def invokeMethod(String name, args) {
DeprecatedInterception.apply(this, name, args)
}
def new_call() {
println('new_call invoked')
}
#Deprecated(instead = 'new_call', more_info = '... the reason')
def depr_call() {
println('depr_call invoked')
}
}
Interception Util
import org.codehaus.groovy.reflection.CachedMethod
class DeprecatedInterception {
static apply(Object owner, String methodName, Object args) {
MetaMethod metaMethod = owner.metaClass.getMetaMethod(methodName, args)
Deprecated d = extractAnnotation(metaMethod)
if (d) {
println("warning: '$methodName' is deprecated. Use '${d.instead()}' instead. More info: '${d.more_info()}'")
}
// handle methods with var-args
metaMethod.isVargsMethod() ?
metaMethod.doMethodInvoke(owner, args) :
metaMethod.invoke(owner, args)
}
static Deprecated extractAnnotation(MetaMethod metaMethod) {
if (metaMethod instanceof CachedMethod) {
metaMethod.getCachedMethod()?.getAnnotation(Deprecated)
} else {
null
}
}
}
Simple Test
Just check that no exceptions/errors..
class TestWarnings {
#Test
void test() {
new SomeClass().with {
new_call()
depr_call()
}
}
}
Output:
new_call invoked
warning: 'depr_call' is deprecated. Use 'new_call' instead. More info: '... the reason'
depr_call invoked
Disclaimer
This should work for most cases, but has some limitations:
will not work for static methods (unless invoked on Object instance)
you have to implement GroovyInterceptable per each class, to apply
you might faced with some side-effects in some groovy syntax or features (at least I've found the issue with vararg methods invocation, but this already fixed)
So this should be tested and potentially improved before widely using for some production projects.
Other options:
Shortly, because implementation might be more complex (not sure, at least I not able to provide some example in a short time), but potentially this is more solid.
Adding AST Transformations.
Use some AOP library.
I have the simple pipeline script:
#!groovy
#org.jenkinsci.plugins.workflow.libs.Library('Some#lib')
import com.cloudbees.groovy.cps.NonCPS
node() {
echo CheekyEnum.getByName('name1').getName()
}
enum CheekyEnum {
ENUM_1('name1', 'f1'),
ENUM_2('name2', 'f2')
String name
String field
CheekyEnum(String name, String field) {
this.name = name
this.field = field
}
static CheekyEnum getByName(String name) {
return values().find { it.name == name }
}
String getName() {
return name
}
}
When I run it everything works OK, but if there is a little change in method getName()
#NonCPS
String getName() {
return name
}
I get a pretty long error stacktrace:
java.lang.StackOverflowError
at java.lang.ClassLoader.loadClass(ClassLoader.java:398)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.lambda$loadClass$0(SandboxResolvingClassLoader.java:51)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.lambda$load$2(SandboxResolvingClassLoader.java:85)
at com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$14(BoundedLocalCache.java:2337)
at java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1892)
at com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2335)
at com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2318)
at com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:111)
at com.github.benmanes.caffeine.cache.LocalManualCache.get(LocalManualCache.java:54)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.load(SandboxResolvingClassLoader.java:79)
...
Why? Doesn't #NonCPS just make method excluded from CPS transformation?
enum is per se a serializable type. So you should better create a wrapper function for it:
import com.cloudbees.groovy.cps.NonCPS
node() {
echo getName(CheekyEnum.getByName('name1'))
}
...
#NonCPS
String getName(CheekyEnum cheeky) {
return cheeky.name
}
The related StackOverflowError could be a bug/smell in the workflow-cps-plugin. Please take a look at its Technical design
Pipeline scripts may mark designated methods with the annotation #NonCPS. These are then compiled normally (except for sandbox security checks).
AFAICS you're running inside a Groovy sandbox. The SandboxInterceptor
is probably generating this stack overflow. Running outside the sandbox should fix your issue as well.
BTW you can also read Pipeline CPS Method Mismatches for a better understanding of what can be called in non-CPS transformed code.
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()
I'm calling a Java method from Groovy which expects an instance of a SAM interface as a parameter.
Normally Groovy is happy with passing in a closure in these cases, and will coerce it accordingly HOWEVER in this case, the interface extends another one and overrides the single method.
Note - It still only has one method, but it's been overriden.
In this instance Groovy doesn't automatically coerce the closure and the only way I seem to be able to call it is by using "AS".
I'm publishing an API to help kids to learn code and really don't want them to have to use "AS" because it would complicate things.
Here's some code that shows the issue...
Java
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
public void handle(String test);
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}
Groovy
LibraryClass bar = new LibraryClass();
bar.method({ name -> println "HELLO " + name})
Error
Caught: groovy.lang.MissingMethodException: No signature of method: Bar.doIt() is applicable for argument types: (testClosures$_run_closure1) values: [testClosures$_run_closure1#fe63b60]
Any help on how to get around this without using "AS" would be hugely appreciated
Groovy wants to implement the interface by coercion, but doesn't know which interface method it should implement. As there are 2:
the handle(String test) and a second one: handle(String test) (of the baseHandler)
The solution is to remove the handle(String test) from the handler (it adds nothing as the BaseHandler posesses this method already thanks to the generics).
Like this it works correctly:
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}