https://kotlinlang.org logo
#konsist
Title
# konsist
c

ceedee

09/21/2023, 8:14 AM
I’m trying to introduce Konsist to a legacy codebase which shall be refactored over time. Especially, for newly implemented features, clean architecture shall be applied. Still, the codebase is not and will not (soon) be migrated to feature modules. Despite of that, I’d like to add checks like
Copy code
Konsist
    .scopeFromProject()
    .assertArchitecture {
        val domain = Layer("Domain", "com.myapp.features.feature01.domain..")
        val data = Layer("Data", "com.myapp.features.feature01.data..")

        domain.dependsOnNothing()
        data.dependsOn(domain)
    }
This approach requires me to create some kind of re-usable test where I can paramterize the package for the feature I want to test. To foster a common project structure, I’d like to just write the test once, like I have only one Detekt configuration for the whole project. But this would require me to define some kind of a baseline like I did for Detekt for dealing with the legacy codebase. Is there a way to do this? Also, I have an additional question regarding incremental development: consider the case that a feature is implemented layer by layer. Then having a test which already requires the presentation layer will always fail and does not allow me to integrate the partial implementation into my codebase:
Copy code
Konsist
    .scopeFromProject()
    .assertArchitecture {
        val domain = Layer("Domain", "com.myapp.features.feature01.domain..")
        val data = Layer("Data", "com.myapp.features.feature01.data..")
        val presentation = Layer("Presentation", "com.myapp.features.feature01.presentation..")

        domain.dependsOnNothing()
        data.dependsOn(domain)
        presentation.dependsOn(domain)
    }
Is there a way to allow this kind of development, e.g. by making the presentation layer optional? Currently it throws
com.lemonappdev.konsist.core.exception.KoPreconditionFailedException: Layer Presentation doesn't contain any files.
I helped myself with whitelisting known violations, but it’s not a nice solution, so I still appreciate any help into that direction. Here’s what I use for the moment:
Copy code
fun KoScope.removeWhitelistedItems(whitelist: List<String>): KoScope {
    return slice { fileDeclaration ->
        whitelist.none { whitelistedItem ->
            fileDeclaration.name == whitelistedItem
        }
    }
}
using it likes this:
Copy code
@Test
    fun `companion object is last declaration in the class`() {
        Konsist
            .scopeFromProject()
            .removeWhitelistedItems(KonsistBaseline.baselineForCompanionObjectIsNotLastDeclaration)
            .classes()
            .assert {
                val companionObject = it.objects(includeNested = false).lastOrNull { obj ->
                    obj.hasModifier(KoModifier.COMPANION)
                }

                if (companionObject != null) {
                    it.declarations(includeNested = false, includeLocal = false).last() == companionObject
                } else {
                    true
                }
            }
    }
with
Copy code
object KonsistBaseline {
    val baselineForCompanionObjectIsNotLastDeclaration = listOf(
        "ClassA",
        "ClassB",
        ...
    )
}
Also I have an extension function to create the baseline:
Copy code
fun <T : KoBaseProvider> List<T>.printBaseline(
    violationCriterion: (T) -> Boolean,
    nameProvider: (T) -> String
): List<T> {
    return print(prefix = "listOf(") {
        if (violationCriterion(it)) {
            "\"${nameProvider(it)}\","
        } else {
            ""
        }
    }
}
That one is also not ideal since I need to add a call to it in my test which is repeating the validation of the test. Apart from that it adds many blank lines where the criterion is not violated.
i

igor.wojda

09/21/2023, 7:52 PM
1. ATM Konsist does not have baseline file, however you can define scope for individuals modules. See https://docs.konsist.lemonappdev.com/writing-tests/koscope#nested-module-scope If module is not granural enough you could create scopes from individual packages. Also take a look at this article that presents very similar to your case Refactoring Multi-Module Kotlin Project With Konsist | by Igor Wojda :robot_face: | Sep, 2023 | ProAndroidDev 2. I think the above approach should also help. I am not sure if testing non-existing things is a good idea, but if you think it is then please explain your development flow in more detail. BTW It is best to ask separate questions in separate messages to have separate discussions.
4 Views