Hey! Welcome to Understanding Gradle. In this
episode, we'll look a bit more at how test reports are generated with Gradle and how
you can configure Gradle to also generate code coverage reports. In our example project, I
have now added tests to both the data-model and the business-logic project. As explained in the
videos on testing and configuring the Test task, we can run the tests in each of these projects
using the Test task and then we'll get reports in the corresponding build folder. So if
we run the :test task of the data-model, we get a test report in the build/reports folder. Showing us which tests were successful and
which failed and the corresponding failures. Running the test task in the business-logic
project, we get a similar report for the tests in the business-logic project. Now, these
reports are directly generated by JUnit. There's a binary version of the report
data that can be picked up by some tools, like Jenkins for example, and there's an html
version rendered by Gradle. I've already shown, when we talked about configuring the Test task,
how you can influence which reports are generated and where these reports are placed. We perform
such configuration in our my-java-base convention plugin. Now another report you might want
to generate is a code coverage report. Such reports tell you, which parts of your
code have actually been touched by your tests. For this, you need additional tools. A common
tool in the java world is JaCoCo - the Java Code Coverage tool. Gradle core directly
supports this if you want this report, as typical with Gradle, you apply another
plugin which in this case is called 'jacoco'. This plugin actually extends the
functionality of the Test task. If applied, you get an extension where you could
configure some details. But you can also just role with the defaults. So only applying this
plugin already gives you code coverage reports. With the plugin applied, run the tests again -
in this example in the business-logic project. We can see how an additional
report has been generated. It shows us which parts of our code
have been touched by our tests. We could generate the same report
now in the data-model project. Imagine now, that you have a larger real project.
In these cases, especially the code coverage report, is something that you often would like
to see aggregated. So you don't want to look at like 50 different reports but want to have
one single report that shows you all the code coverage of your complete code base. And similar,
also the normal test report, you might want to see in an aggregated view. In the video about
aggregating custom artifacts, we already talked about use cases like this. There we explored,
how you can generate an aggregated Javadoc of your complete code base. In that video, we used
variant-aware dependency management features, like consumable and resolvable Configurations,
because that's something where Gradle does not offer a direct solution out-of-the-box. For the
test result or code coverage report aggregation, we could do something similar. But since
recent Gradle 7 versions, this is supported more directly by Gradle: by offering plugins
that do most of the setup for you. Always when you aggregate something, you need to decide
in which project this aggregation happens. So for the Javadoc, we picked our 'app' project
to do the aggregation there (in the other video). This is a good choice in a setup like ours,
because the 'app' project is a place where also all the code is aggregated. So the 'app' project
already has dependencies to all other components of our software. So if we want to collect some
report or verification data from all of our projects we already have a dependency setup
where all these projects are reached through the dependency graph. So in a project setup like
this, we can now go to the convention plugin for our 'app' project and there we now just apply two
additional plugins: The 'test-report-aggregation' plugin and the 'jacoco-report-aggregation' plugin.
Without doing any further configuration, Grader can now provide us with aggregated test reports
and aggregated code coverage reports. We can see that additional tasks have been added to the 'app'
project in the verification group. Calling one of these tasks, will generate the corresponding
aggregated report. This will also automatically trigger the execution of all the tests, because
the report data is output of the Test task. So typically you might want to wire these tasks
into one of your lifecycle tasks now - for example the :check task. The aggregated report can now be
found in the build folder of the 'app' project. For a pretty standard setup, this might
already be sufficient for your build and with these new plugins by Gradle, the
setup is rather straightforward. Still, you might want to do further customization. And
then you quickly get to a point where you need a better understanding of what's happening in
the background. One situation where you possibly want to customize things further is when you have
tests in multiple source sets. In the "configuring testing" video we used integrationTest as
an example of an additional source set or an additional test suite. So for example, if we
would configure an integrationTest source set with the corresponding integrationTest test
suite in our base convention plugin like this, then we would get an additional
aggregation task in the 'app' project for integration test reporting. So the integration
test reporting, both normal test reports and code coverage, is completely separated from the
test reporting (which is about the tests in the 'test' source set only). A customization
you could do now is to also combine all these reports into one. The use case would be to get
the code coverage of all the combined tests. For this we can, for example, go and extend
the inputs of the :testCodeCoverageReport task (the one that for now only looks
at what's happening in the 'test' source set) to also include the integration
test execution data. The execution data is something the JaCoCo tool generates. The
testCodeCoverageReport now is configured to take all the code coverage data that
comes from the tests in the test source set. We can add more data here, because
executionData is an input of the task and we can find that there is a configuration called
integrationTestCodeCoverageReportExecutionData. This is set up by the aggregation plugins where
all the execution data of the integration test is collected. So we add this as an additional
input here and one thing we need to tweak is that we need an artifactView with linient set to
"true". Because otherwise, with the dependency setup we have, Gradle would also look for
execution data in external dependencies. For more details on this, check out the
video about 'aggregating custom artifacts'. With this setup, the testCodeCoverageReport
task will now trigger the execution of the test and the integration test tasks and collect all
the coverage data from all the test executions. But this is only one example of how you could
customize the setup given by Gradle. The aggregation plugins we applied here are only
convenience. They use public Gradle API to do all the setup they are doing. So as with other
concepts, like Feature Variants, these plugins just make it convenient to get a working solution
quickly for most cases. But internally, they also only use public Gradle dependency management APIs.
So all the things the plugins do, is something you could do step-by-step yourself in your build
scripts, if you want a very customized setup. In the video about 'Feature Variants', I demonstrated
this on the example of Feature Variants. If you get into a situation where you want to do
such customizations, there is another help task I would like to show you that can help you. This
task is called :outgoingVariants and can be found in the help tasks group. If you execute this
task on a project, for example the business-logic project, you can see all the outgoing variants of
this project. An outgoing variant is technically the same as a consumable Configuration. So if you
add a consumable Configuration, you will get a new outgoing variant. And these are all the things
that the project exports that other projects can then pick up. For example, here we can see that
the project now has a variant that exports the source code of the project. This is something we
added manually when we wanted to aggregate Javadoc in the 'aggregating custom artifacts' video. For
more background about all of this, have a look at my video about 'declaring dependencies' and
my video about 'aggregating custom artifacts'. This video should give you a quick start if
you wonder about how to set up code coverage reporting or tweak test reporting in general
with Gradle. The topic is also another example of aggregating artifacts with Gradle. So you
can explore it to understand the topic more. On the other hand, if you want to do more specific
customizations, you need a bit of understanding of Gradle's variant-aware dependency management
to tweak things for your custom requirements. If you enjoyed this video, please subscribe
to this channel to get notified when new videos are available. In addition,
you can also follow me on Twitter and check out the additional resources i
am linking here. See you next time.