I have created two classes:
#ExperimentalCoroutinesApi
open class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance()
.setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
override fun postToMainThread(runnable: Runnable) = runnable.run()
override fun isMainThread(): Boolean = true
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
and
#ExperimentalCoroutinesApi
class CoroutineExecutorExtension : InstantExecutorExtension() {
private val testCoroutineDispatcher = TestCoroutineDispatcher()
override fun beforeEach(context: ExtensionContext?) {
super.beforeEach(context)
Dispatchers.setMain(testCoroutineDispatcher)
}
override fun afterEach(context: ExtensionContext?) {
super.afterEach(context)
Dispatchers.resetMain()
testCoroutineDispatcher.cleanupTestCoroutines()
}
}
I'm using these classes to write unit tests for method that use coroutines and livedata.
The problem I have is that I don't know how to share these classes in different modules.
If I define them in the test folder then I can build the classes but they are not visible in other modules.
If I define in the main folder there are compile time errors.
What can I do to avoid having to define these classes in all the modules?
UPDATE
To follow #Sam suggestion I have created a module that contains the two classes in the folder:
.\unittest\src\testFixtures\java\com\name\terminal\unittest
The build.gradle file of this module uses the plugins:
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'java-test-fixtures'
}
In the module where I want to use the CoroutineExecutorExtension I have imported the project of the unittest module with:
testImplementation(testFixtures(project(":unittest")))
The problem is that the classes in the testFixtures folder are not visiible.
EDIT
This method does not work for an Android library. I can't use a java library because I need to be able to include Androidx dependencies.
If you're using Gradle, the test fixtures plugin is an appropriate solution. The plugin is built in, so you can simply add java-test-fixtures to your plugins block.
plugins {
`java-test-fixtures`
}
This automatically creates a new testFixtures source set, alongside the existing main and test source sets that you already have. Code that you write inside src/testFixtures/kotlin will be visible from your tests, and will have access to everything in main. Anything that the test fixtures need (for example, dependencies on kotlinx-coroutines or your test framework) should be added as dependencies of the test fixtures.
You can also also add dependencies on test fixtures from another module. For example, if module-b needs to use the test fixtures from module-a, then in the Gradle build script for module-b you could add:
dependencies {
testImplementation(testFixtures(project(":module-a")))
}
The examples I've given are for Kotlin build scripts, but you can find the Groovy equivalents in the linked documentation.
Related
I work on an Android project using multiple Project Flavors, and we use Koin to inject the appropriate dependencies based on the current flavor.
We already use the checkModules Gradle task (described here : https://start.insert-koin.io/#/getting-started/testing?id=checking-your-modules) in order to ensure that our dependency tree is valid.
However, it seems that there is a use case missing.
Let's say I want to inject an InterfaceA in my Activity. I would write the following code :
class MyActivity : Activity() {
private val interfaceA_Impl: InterfaceA by inject()
...
}
Koin requires the implementation of InterfaceA to be provided in a module, as such :
val myModule = module {
single<InterfaceA> { MyInterfaceImpl() }
}
In my project, each implementation is "flavor-specific".
My question is :
Is there a way to ensure that ALL by inject targets are valid ? In other words, to ensure that all interfaces that I am trying to inject have valid implementations ? Currently, if an implementation is forgotten, the app crashes during runtime, and I would like to know about it sooner (maybe during unit tests, at the same time checkModules is ran ?)
Thanks a lot !
In my project, I'd like to generate a class that contains information about my dynamic features. Dynamic features are added this way:
// In the base module’s build.gradle file.
android {
...
// Specifies dynamic feature modules that have a dependency on
// this base module.
dynamicFeatures = [":dynamic_feature", ":dynamic_feature2"]
}
Source: https://developer.android.com/guide/app-bundle/at-install-delivery#base_feature_relationship
I've been searching for solutions since a few days now, and I didn't find much. Currently, my plugin looks like this:
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
if (project == rootProject) {
throw Exception("This plugin cannot be applied to root project")
}
val parent = project.parent ?: throw Exception("Parent of project cannot be null")
val extension = project.extensions.getByName("android") as BaseAppModuleExtension?
?: throw Exception("Android extension cannot be null")
extension.dynamicFeatures
}
}
Unfortunately, extension.dynamicFeatures is empty even if my plugin is applied to the build.gradle file having dynamic features.
It's empty, because you are trying to get extension value at the gradle lifecycle configuration stage, all gradle properties have not configured yet.
Use afterEvaluate closure. In this block dynamicFeatures has already configured and not empty.
project.afterEvaluate {
val extension = project.extensions.getByType(BaseAppModuleExtension::class.java)
?: throw Exception("Android extension cannot be null")
extension.dynamicFeatures
}
I'm writing a custom Gradle plugin which should be able to access the configuration parameters from the Android plugin.
I can do certainly do this from a groovy plugin (which is my current code):
MyPlugin.groovy:
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.android.applicationVariants.all { variant ->
variant.registerJavaGeneratingTask( // all the right parameters
My problem is that I don't want the plugin to be in Groovy, I want it using Kotlin.
Simply typing project.android in Kotlin does not work, as far as I understand I would need to write something similar to what's below:
MyPlugin.kt
import com.android.build.gradle.AndroidBasePlugin
class MyPlugin : Plugin<Project> {
override fun apply(target: Project) {
val android = target.plugins.findPlugin(AndroidBasePlugin::class.java)
// and call android here
My main problem is that that import import com.android. does not exist.
I tried adding lots of different dependecies on build.gradle.kts, like example implementation("com.android.tools.build:gradle:3.6.1") but nothing gives me access to it.
questions:
is that the right approach?
case:
(yes) 2. How do I import and use it?
(no) 2. What is the right approach?
tl;dr:
how to write project.android.applicationVariants.all { variant -> inside a Gradle Kotlin plugin
edit:
#tim_yates answer worked perfectly. To whom is interested, here is the final code.
build.gradle.kts:
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
implementation("com.android.tools.build:gradle:3.6.1")
... my other dependencies
}
package-name/MyPlugin.kt:
import com.android.build.gradle.AppExtension
import com.android.build.gradle.api.ApplicationVariant
(...)
override fun apply(target: Project) {
val android = target.extensions.findByType(AppExtension::class.java)
android?.applicationVariants?.configureEach {
configureVariant(target, this)
}
private fun configureVariant(...) { // register my taks there
}
What you'll need is something like this:
project.extensions.configure<AppExtension>("android") {
it.applicationVariants.all {
it.registerJavaGeneratingTask( // all the right parameters
}
}
And you'll need the com.android.tools.build:gradle dependency as you say.
Why it's not being recognized, I'm not sure... Are you writing a standalone plugin? If so, what you have works... If it's an inline plugin in a buildSrc folder, then you'll need to add the lib to the buildSrc build
How to add a debug-mode specific functionality into an Android app which is broken into several SDKs?
Lets say I need to add an OkHttp interceptor to all OkHttp clients to all SDKs. But it has to be added only into the app's debug build. However, when the app is broken into several SDKs, they are published as release builds even if the main app is built in the debug mode. Thus, the debug-specific interceptors inside the SDKs would not be added to the OkHttp clients.
How to overcome this issue? I was thinking to take advantage of dependency injection (Koin) and pass Build type information from the app where Koin is started, but not sure Koin supports this feature.
You can select what build variant is active of each of the modules of your application.
https://developer.android.com/studio/build/build-variants
You can use getAll() in Koin for your purposes.
moduleb:
Domain:
interface Interceptor
class InterceptorFactory(val interceptors: List<Interceptor>) // Here you have a list of all interceptors from all modules
class HeadersInterceptor : Interceptor
Koin:
object BKoin {
val network2: Module
get() = module {
single<HeadersInterceptor>() bind Interceptor::class
single<InterceptorFactory> {
InterceptorFactory(getAll<Interceptor>())
}
}
}
module app:
define your objects
class OtherInterceptor : Interceptor
declare them
object AKoin {
val network1: Module
get() = module {
single<OtherInterceptor>() bind Interceptor::class
}
}
init Koin
startKoin {
val app = modules(
listOf(
BKoin.network2,
AKoin.network1
)
)
Log.d("TUT", "${app.koin.get<InterceptorFactory>().interceptors.map { it::class.simpleName }}")
}
Gradle:
dependencies {
implementation project(':moduleb')
}
I am currently trying to write a custom lint check that I am working on. I have created a separate java project and included it as a jar.
My problem is that no matter what, it seems that my custom check is not being run when analyzing my code base.
I've included a registry
class MyIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
get() = listOf(MyIssues.ISSUE_PATTERN)
}
And a detector
class MyIssueDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UClass::class.java)
override fun createUastHandler(context: JavaContext) =
MyIssueDetector(context)
class MyIssueDetector(private val context: JavaContext) : UElementHandler() {
override fun visitClass(node: UClass) {
context.report(MyIssues.ISSUE_PATTERN, context.getNameLocation(node), "This is just a test")
}
}
}
I've also added attributes("Lint-Registry-v2": "com.pathto.lint.MyIssueRegistry") to my java project's gradle and included it in my app gradle as lintChecks project(":lint")
AFAIK this topic- My code should be throwing a warning everytime it reads a class, but the lint check is not being ran. Is there a step I am missing?
first, check your lint.jar is placed on the right path. my path is ~/.android/lint/lint.jar.
then execute command '$ANDROID_HOME/tools/bin/lint --show | grep 'your issue name'' to check your custom lint is attached or not.
and then you can put some log to your custom lint implementation just like using "System.out.println()"
hope this can help u.