How to configure dagger to inject groovy classes, and to inject into groovy classes?
I was initially trying to get dagger to inject a groovy class into my java app, and I found dagger was complaining the groovy class is not found. Looking at the log, it seems that compileGroovy happens after compileJava. And the annotation processing of dagger compiler seems to be in compileJava. I guessed that might be the problem -- no groovy classes are available at this time. But I've yet figured out a way to coerce either of dagger or groovy to work with the other.
It seems I could not upload a .tar.gz. But if anyone needs a minimal demo code for what I meant to achieve, these might help (with gradle 7):
build.gradle:
plugins {
id 'groovy'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'com.google.dagger:dagger-compiler:2.+'
implementation 'com.google.dagger:dagger:2.+'
implementation 'org.codehaus.groovy:groovy-all:3.+'
}
settings.gradle:
rootProject.name = 'groovy-dagger1'
src/main/groovy/org/example/dagger/MainComponent.groovy:
package org.example.dagger
import dagger.Component
#Component(modules = [
MainModule,
])
interface MainComponent {
String message();
}
src/main/groovy/org/example/dagger/MainModule.groovy:
package org.example.dagger
import dagger.Module
import dagger.Provides
#Module
final class MainModule {
#Provides
static String message() {
return 'Hello Groovy Dagger!'
}
}
src/main/groovy/org/example/main/Main.groovy:
package org.example.main;
class Main {
static void main(String[] args) {
// Dagger component does not exist :/
// println DaggerMainComponent.create().message()
}
}
By default, the groovy compiler will not run the java annotation processors...
You can add this to your build.gradle:
compileGroovy {
groovyOptions.javaAnnotationProcessing = true
}
You will of course need to add an import
import org.example.dagger.DaggerMainComponent
To Main.groovy
Related
Maybe this is very simple, but I couldn't find any examples on the web:
I'd like to use JUnit 5 to run a unit test implemented as a Groovy class. My current setup seems to launch JUnit 5, but fail to detect the test case. IntelliJ recognizes the test, but fails to run it. If I add a Java unit test, it is launched correctly.
Here's what I have now:
Project structure
src
main
groovy
# production code
test
groovy
UnitTest.groovy
build.gradle
...
build.gradle
plugins {
id 'groovy'
}
dependencies {
compile localGroovy()
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1'
}
test {
useJUnitPlatform()
}
UnitTest.groovy
import org.junit.jupiter.api.Test
class UnitTest {
#Test
def shouldDoStuff() {
throw new RuntimeException()
}
}
I'm using Gradle 4.10.
Any ideas?
JUnit requires all testing method to use return type void. Groovy's def keyword is compiled to an Object type, so your method compiles to something like this in Java:
import org.junit.jupiter.api.Test
public class UnitTest {
#Test
Object shouldDoStuff() {
throw new RuntimeException();
}
}
If you try this out as a Java test, it won't find the test case neither. The solution is very simple - replace def with void and your Groovy
test case will be executed correctly.
src/test/groovy/UnitTest.groovy
import org.junit.jupiter.api.Test
class UnitTest {
#Test
void shouldDoStuff() {
throw new RuntimeException()
}
}
Demo:
I have no idea why this error exists:
Program type already present: org.hamcrest.CoreMatchers
Message{kind=ERROR, text=Program type already present: org.hamcrest.CoreMatchers, sources=[Unknown source file], tool name=Optional.of(D8)}
My code in in the scope if dependency of build.gradle (Module: app) is:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation ('junit:junit:4.12'){
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.monkeylearn:monkeylearn-java:0.1.4'
}
MainActivity:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.monkeylearn.MonkeyLearn;
import com.monkeylearn.MonkeyLearnException;
import com.monkeylearn.MonkeyLearnResponse;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
MonkeyLearn ml = new MonkeyLearn("*******************************");
String moduleId = "*********";
String[] textList = {"This is a text to test your classifier", "This is some more text"};
MonkeyLearnResponse res = ml.classifiers.classify(moduleId, textList, true);
System.out.println( res.arrayResult );
} catch (MonkeyLearnException e) {
e.printStackTrace();
}
}
}
Do you have any ideas?
There is a similar problem with simple-json in another question that I answered here.
I suggest you do the same for monkeylearn-java and junit or any other non-google dependencies one by one, I mean download their jar files and put them in the libs folder one by one, to find out which one is the problem, and leave it be in libs folder.
I think this is a bug either in Android Studio or in Gradle.
I had a similar problem with simple-json. For that the solution was to exclude the JUnit dependency.
I am not an expert on Android programming but I find it strange that you exclude hamcrest-core from the JUnit testImplementation. I would rather suggest to exclude transitive dependencies from external libs.
For simple-json this was my solution:
implementation('com.googlecode.json-simple:json-simple:1.1.1') {
exclude group: 'junit', module: 'junit'
}
Maybe you can do the same for monkeylearn-java?
I have a strange behaviour with mockito 2 and Junit 5 : Mockito can't mock a class.
I extracted my test to a simple test case :
#ExtendWith(MockitoJavaExtension.class)
class JavaTest {
#Test
void shouldMockClass(){
final MockedJavaClass mock = mock(MockedJavaClass.class);
when(mock.execute()).thenReturn(Collections.singletonList("some value"));
assertEquals(1, mock.execute().size());
}
#Test
void shouldMockInterface(){
final MockedJavaInterface mock = mock(MockedJavaInterface.class);
when(mock.execute()).thenReturn(Collections.singletonList("some value"));
assertEquals(1, mock.execute().size());
}
}
class MockedJavaClass{
List<String> execute(){
throw new IllegalArgumentException();
}
}
interface MockedJavaInterface{
default List<String> execute(){
throw new IllegalArgumentException();
}
}
When I run this test, I get the IllegalArgumentException :
JUnit Jupiter:JavaTest:shouldMockClass()
MethodSource [className = 'JavaTest', methodName = 'shouldMockClass', methodParameterTypes = '']
=> java.lang.IllegalArgumentException
MockedJavaClass.execute(JavaTest.java:36)
JavaTest.shouldMockClass(JavaTest.java:19)
It means that the class is not mocked at all.
I also tried with an external class (not an inner one), and the issue is still the same...
This is also my build.gradle :
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.junit.platform:junit-platform-gradle-plugin:1.1.1"
}
}
apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'org.junit.platform.gradle.plugin'
sourceCompatibility = 8
targetCompatibility = 8
repositories {
mavenCentral()
}
dependencies {
testCompile "org.mockito:mockito-core:2.18.0"
testCompile "org.mockito:mockito-junit-jupiter:2.18.0"
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
testCompile("org.junit.jupiter:junit-jupiter-params:5.1.1")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
testRuntime("org.junit.platform:junit-platform-launcher:1.1.1")
}
junitPlatform {
filters {
engines {
include 'junit-jupiter'
}
includeClassNamePattern '.*Test'
}
}
I also tried with the mockito junit5 extension to inject mocks, but I still have the problem...
Any help will be appreciated !
I finally ended finding the solution : My first test was a Kotlin one, in a "open class" but a non-opened method declaration.
Mockito can't mock a method with a package access nor a final method !
In this particular case, it just calls the real method when you try to mock it, without telling you anything...
Maybe this post will be useful for someone ! –
You didn't mock the execute function on your MockClass so it used the implementation of execute that was in the class definition - in other words the IllegalArgumentException.
You need to setup your mocks such that they provide dummy implementations of the methods that are going to be required as dependancies to your test case. In this particular case it doesn't appear you are testing anything really, just learning Mockito?
So yea, the answer is - provide mock implementations or the default will be used.
I have a Gradle build script, that grew too big so I made a utility class.
In this class I want to use the Gradle fileTree (or any other Gradle class), how can I do it?
To be clear, this is in build.gradle:
ext {
utils = new Utils()
}
and in Utils.groovy (which is in buildSrc/src/main/groovy):
def chopBackgroundImage(String inPath, String outPath, int scale) {
new File(outPath).mkdirs();
def tree = fileTree(dir: inPath, include: '*.png') // doesnt work
}
fileTree is a method defined on Project interface so there's a need to pass project instance to method and import Project class in Utils. Utils should look like this:
import org.gradle.api.Project
public class Utils {
def chopBackgroundImage(Project project, String inPath, String outPath, int scale) {
new File(outPath).mkdirs();
def tree = project.fileTree(dir: inPath, include: '*.png')
}
}
To make Project accessible in buildSrc modify build.gradle by adding the following content:
buildscript {
dependencies {
gradleApi()
}
}
And - of course - because of the fact that groovy is a dynamic language chopBackgroundImage can be defined in the following way:
def chopBackgroundImage(project, inPath, outPath, scale) {
new File(outPath).mkdirs()
def tree = project.fileTree(dir: inPath, include: '*.png')
}
No dependencies needed! ;)
groovy defualt imports
Is it possible to have our own package in default imports? Is there any way to tell the groovy runtime to use my own package as default import?
This JIRA covers you question.
The bulk of it is here.
class DefaultImportSourceUnitOperation extends SourceUnitOperation {
public void call(SourceUnit source) throws CompilationFailedException {
source.getAST().addImportPackage("pkg1.pgk2.pkg3.");
}
}
class DefaultImportClassLoader extends GroovyClassLoader {
protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource
codeSource) {
CompilationUnit cu = super.createCompilationUnit(config, codeSource)
cu.addPhaseOperation(new DefaultImportSourceUnitOperation(), Phases.CONVERSION)
return cu
}
}
Be sure not to forget to add the . to the end of the package declaration.
Goodluck!
Since Groovy 1.8 there is an easier way with ImportCustomizer:
def importCustomizer = new ImportCustomizer()
importCustomizer.addImports('com.looneytunes.Coyote', 'com.looneytunes.RoadRunner')
importCustomizer.addStarImports('com.acme')
importCustomizer.addStaticStars('com.looneytunes.Physics')
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(importCustomizer)
def shell = new GroovyShell(configuration)
shell.evaluate """
new Coyote().startPlanToCatch(new RoadRunner()) // no need for explicit imports
.useContraption(new PortableHole()) // from com.acme
.withPhysicsViolation({ forgetGravity(it) }) // method from com.looneytunes.Physics
.start()
"""