Get coverage from class in other module tested with QuarkusUnitTest - gradle

In the jacoco coverage of my quarkus extension, I am missing certain classes. The project contains to submodules, which finally causing the issue.
Example project: https://github.com/HerrDerb/quarkus-extension-coverage-issue
As I cannot run QuarkusTest in a quarkus extension runtime module, I test certain runtime classes with a QuarkusUnitTest in my deployment module. These beans under test are missing in the final coverage report of the deployment module
I am testing the class MyBean inside of the deployment module. When running ./gradlew test jacocoTestReport it will generate the folder deplyoment/build/reports/jacacoco. Looking over the html reports files, it is visible that the coverage report of MyBean is missing. This is, because the bean is actually not implemented inside of the deployment module. Moving it test-wise from the runtime module to the deployment module will result in the desired coverage.
I tried to play around with jacoco properties inside the debployment/build.gradle which did not result in any success:
jacocoTestReport {
additionalSourceDirs.from project(':runtime').sourceSets.main.java.srcDirs
}
How can I include "external" beans (from another module) in the coverage report of my QuarkusUnitTest?

Related

How can I access the dependencies of an application from within the build file of a dependency embedded in the application?

I have a Gradle-based library that is imported as a dependency into consuming applications. In other words, an application that consumes my library will have a build.gradle file with a list of dependencies that includes both my library as well as any other dependencies they wish to import.
From within my library's build.gradle file, I need to write a Gradle task that can access the full set of dependencies declared by the consuming application. In theory, this should be pretty straightforward, but hours of searching has not yielded a working solution yet.
The closest I've come is to follow this example and define an additional task in the library's build.gradle file that runs after the library is built:
build {
doLast {
project.getConfigurations().getByName('runtime')
.resolvedConfiguration
.firstLevelModuleDependencies
.each { println(it.name) }
}
}
I keep getting an error message that the 'runtime' configuration (passed into getByName and referenced in the Gradle forum post I linked) cannot be found. I have tried other common Gradle configurations that I can think of, but I never get any dependencies back from this code.
So: what is the best way to access the full set of dependencies declared by a consuming application from within the build file of one of those dependencies?
Okay, I mostly figured it out. The code snippet is essentially correct, but the configuration I should have been accessing was 'compileClasspath' or 'runtimeClasspath', not 'runtime'. This page helped me understand the configuration I was looking for.
The final build task in the library looks roughly like this:
build {
doLast {
// ...
def deps = project.getConfigurations().getByName('compileClasspath')
.resolvedConfiguration
.firstLevelModuleDependencies
.each {
// it.name will give you the dependency in the standard Gradle format (e.g."org.springframework.boot:spring-boot:1.5.22.RELEASE")
}
}
}

Maven JUnit 5: Using JUnit5 tags, run all unit tests by default or run only a specific test suite from the command line

I posted this: Maven JUnit 5: Run all unit tests by default or run only a specific test suite from the command line .
I'm simply posting it again with almost exactly the same title because nothing about my base problem has changed, but I will include what I tried to do with Tags, which are not working for me.
We are upgrading our services to a relatively recent SpringBoot version, along with JUnit 5. We normally run our builds with just "mvn package", which runs all of our unit tests by default.
We also have "component tests", which are run from a separate command line, and which are all specified in a single test suite with a specific name. Before JUnit 5, we would run this with something like:
mvn -Dtest=ComponentTestSuite test
This was working fine with JUnit 4.
The class looks like this:
#Suite
#SelectClasses(<testclassname>.class)
public class ComponentTestSuite {
}
With JUnit 5, this ends up saying "No tests found".
We are using v2.3.12 of Spring-Boot, which by default includes a somewhat older version of JUnit 5. I am overriding those defaults and including v1.8.2 of the junit-platform components and v5.8.2 of the jupiter components.
For the service that I'm testing this with, the test suite only has a single component test (which is unusual). I WAS able to simply replace "ComponentTestSuite" with the name of the component test class, and that would run that single component test.
I noticed this thread: Junit5 test suites with #Suite annotation doesn't execute tests with mvn test command .
Unfortunately, what I found was that changing "-Dtest" to "-Dinclude" simply ran all my default unit tests and ignored the test suite.
So, I was told that I'm "not supposed to do it this way", although it's not clear to me exactly which parts I'm not supposed to do, but I am supposed to use "Tags". Ok, so I tried to use tags.
I also noticed this article: https://www.baeldung.com/junit-filtering-tests , although that article appears to use some deprecated mechanisms, like "#RunWith(JUnitPlatform.class)", so I'm not sure how much I can believe from that article.
I've been trying several variations, and my last attempt is this:
#Suite
#Tag("ComponentTest")
#IncludeTags("ComponentTest")
public class ComponentTestSuite {
}
And I added #Tag("ComponentTest") to my one component test class.
This still doesn't work. I try to run mvn -Dtest=ComponentTestSuite test, and it says "No tests found". If I instead try mvn -Dtests=ComponentTest test that just runs all of my unit tests and ignores my component test suite. If I instead run mvn test -Dgroups=ComponentTest, that gives me another variation of "No tests found" in that it doesn't print that, it just literally executes no tests without giving an error.
Looking at that suite class now, it does seem nonsensical, as it doesn't really "contain" anything anymore, which is unfortunate. It was good to have that list of component test classes in one place. Now, it seems like the suite class is pointless, but I can't get anything to work anyway.
Note that I haven't yet added a "groups" element to my surefire config, and I haven't edited all of my unit tests to add a #Tag("UnitTest") annotation. I've only edited the suite class and the one component test.
Update:
To address a comment, I am specifying the following dependencies (among others):
Version 1.8.2 of
junit-platform-suite
junit-platform-suite-api
junit-platform-launcher
junit-platform-commons
junit-platform-engine
junit-platform-runner
junit-platform-suite-engine
Version 5.8.2 of
junit-jupiter
junit-bom (probably unnecessary)
The entire suite class is this:
import org.junit.jupiter.api.Tag;
import org.junit.platform.suite.api.IncludeTags;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
#Suite
#Tag("ComponentTest")
#IncludeTags("ComponentTest")
public class ComponentTestSuite {
}

How to make Kotlin `internal` objects accessible to tests?

My project uses several Gradle source sets for its production code base instead of just main:
domain
dal
rest
test
dbUnitTest
This has proven very useful for limiting dependencies and enforcing separation of concern.
It comes with one downside however: we cannot access classes or methods with visibility internal from within test classes. The reason for this is that the Kotlin compiler places every source set in its own "module":
$ find . -name '*.kotlin_module'
./classes/kotlin/domain/META-INF/contact-management_domain.kotlin_module
./classes/kotlin/dal/META-INF/contact-management_dal.kotlin_module
./classes/kotlin/rest/META-INF/contact-management_dal.kotlin_module
./classes/kotlin/test/META-INF/contact-management.kotlin_module
./classes/kotlin/dbUnitTest/META-INF/contact-management_dbUnitTest.kotlin_module
I would like all sourceset to use the same module name "contact-management", as the main sourceset would by default.
I tried to override the name with the compiler option -module-name:
tasks.withType<KotlinCompile> {
kotlinOptions {
// place all source sets in the same kotlin module, making objects with 'internal' visibility available to every source sets of this project
freeCompilerArgs += listOf("-module-name \"contact-management\")
}
}
Upon running gradlew build, I get
> Task :contact-management:compileDomainKotlin FAILED
e: Invalid argument: -module-name "contact-management"
The reason being that -module-name "contact-management_domain" is set before by the Gradle code invoking the Kotlin compiler as well, but apparently this option is only accepted once.
In a Gradle build, how can I control what is being considered "one module" by the Kotlin compiler?
A related question where the test source set is to be split has no satisfactory answers so far.
You can do that using kotlin compilations. (As far as I understand, a compilation is simply a block of files that are compiled together. A good explanation can be found here)
When you create a sourceset in gradle, the kotlin plugin creates a compilation under the hood (with the same name as the sourceset).
What you can do now with compilations is create associations. If a compilation A is associated with another compilation B, source code in A gets access to internal code units of B.
So in your case, if the test sourceset should get access to the dal sourceset you can simply associate the test compilation with the dal compilation:
kotlin.target.compilations.getByName("test").associateWith(kotlin.target.compilations.getByName("dal"))
PS: It also works the other way around. If you create compilations explicitly, the corresponding sourcesets are created under the hood. So for custom sourcesets you can create compilations and associate them:
val domainCompilation = kotlin.target.compilations.create("domain")
val dalCompilation = kotlin.target.compilations.create("dal") {
associateWith(domainCompilation)
}
In above example, the sourceset domain will have access to internal code units of the sourceset dal.

Can't configure properties-file in gradle

So I'm trying to access a token key in my Kotlin code, but it won't let me import BuildConfig.
In my main() I have:
val client: DiscordClient = DiscordClientBuilder(BuildConfig.TOKEN_KEY).build()
BuildConfig is red in IntelliJ.
My build.gradle is as follows:
defaultConfig {
buildConfigField("String", "TOKEN_KEY", apikeyProperties['TOKEN_KEY'])
}
Everywhere I look I see stackoverflow posts where they want defaultConfig to be in android {}, but I'm not making an Android app.
My token key is in a file called apikey.properties in root which has been added to .gitignore.
Content of the file:
TOKEN_KEY="token_value"
BuildConfig is a class generated by the Android Gradle plugin, hence it's not part of the Gradle's standard featrue set.
At build time, Gradle generates the BuildConfig class so your app code can inspect information about the current build.
(From: Share custom fields and resource values with your app's code)
Since you're not using Android plugin the class doesn't get generated and is red in IJ.
You have the following options:
Deploy the properties file with your source code and read it accordingly during runtime
Have a look at this answer to the question Generate a Java class using Gradle for Java plugin. It mentions the alternative gradle-buildconfig-plugin for non-Android projects.

Can I use OSGi Mocks with Declarative Services Annotations

I'm trying to test an OSGi service annodated with Declaratice Services annotations (org.osgi.service.component.annotations). I have generated my project based on the AEM Multi-Project Example.
public class PostServiceTest {
#Rule
public AemContext context = new AemContext((AemContextCallback) context -> {
context.registerInjectActivateService(new PostService());
}, ResourceResolverType.RESOURCERESOLVER_MOCK);
#Test
public void shouldFetchRandomPosts() {
final PostService postsService = context.getService(PostService.class);
final List<Post> posts = postsService.randomPosts(100);
assertEquals(100, posts.size());
}
}
Whenever I run this test in IntelliJ, OSGi Mocks complain about hte absence of SCR metadata on the tested class.
org.apache.sling.testing.mock.osgi.NoScrMetadataException: No OSGi SCR metadata found for class com.example.PostServiceTest
at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.injectServices(OsgiServiceUtil.java:381)
at org.apache.sling.testing.mock.osgi.MockOsgi.injectServices(MockOsgi.java:148)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:153)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:168)
at com.example.PostServiceTest.shouldReturnTheEnpointNamesWithValidConfigurationAsTheListOfAcceptableKeys(PostServiceTest.java:23)
Does this mean I can only test classes annotated with the older SCR annotations that come with Apache Felix? The documentation for OSGi Mocks suggests that Declarative Services annotations is supported in version 2.0.0 and higher. The version I'm using meets this criterion.
Interestingly enough, this only happened when I ran the test directly form the IDE. It turns out that IntelliJ was not generating the SCR metadata when compiling my classes for tests.
When I compile the class under test with Gradle, the 'com.cognifide.aem.bundle' plugin is used to generate the SCR descriptor and place it in the resulting Java Archive. That's why unit tests executed by Gradle work fine. Just clicking the Run button in IntelliJ caused this step to be missed.
In order to get this working, I ended up setting up IntelliJ to allow me to run unit tests via Gradle.
I went to Settings > Build, Execution, Deployment > Gradle > Runner and used the dropdown menu so that I could decide whether to use Gradle on a test-by-test basis.

Resources