{"id":23329015,"url":"https://github.com/nidi3/code-assert","last_synced_at":"2025-08-19T00:04:32.750Z","repository":{"id":37270971,"uuid":"48196746","full_name":"nidi3/code-assert","owner":"nidi3","description":"Assert that the java code of a project satisfies certain checks.","archived":false,"fork":false,"pushed_at":"2022-06-20T22:40:24.000Z","size":1443,"stargazers_count":100,"open_issues_count":25,"forks_count":10,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-01T23:39:23.930Z","etag":null,"topics":["architecture-as-code","code-analysis","code-quality","java","kotlin","test-coverage"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nidi3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["nidi3"]}},"created_at":"2015-12-17T20:30:58.000Z","updated_at":"2024-03-31T14:17:07.000Z","dependencies_parsed_at":"2022-09-04T15:10:38.063Z","dependency_job_id":null,"html_url":"https://github.com/nidi3/code-assert","commit_stats":null,"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nidi3%2Fcode-assert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nidi3%2Fcode-assert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nidi3%2Fcode-assert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nidi3%2Fcode-assert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nidi3","download_url":"https://codeload.github.com/nidi3/code-assert/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230647324,"owners_count":18258851,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture-as-code","code-analysis","code-quality","java","kotlin","test-coverage"],"created_at":"2024-12-20T21:30:18.946Z","updated_at":"2024-12-20T21:30:49.164Z","avatar_url":"https://github.com/nidi3.png","language":"Java","readme":"# code-assert\n\n[![Build Status](https://travis-ci.org/nidi3/code-assert.svg?branch=master)](https://travis-ci.org/nidi3/code-assert)\n[![codecov](https://codecov.io/gh/nidi3/code-assert/branch/master/graph/badge.svg)](https://codecov.io/gh/nidi3/code-assert)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/guru.nidi/code-assert/badge.svg)](https://maven-badges.herokuapp.com/maven-central/guru.nidi/code-assert)\n\nAssert that the source code of a project satisfies certain rules.\n\nNobody follows rules that are not checked. \nIf they are only checked periodically / manually by an \"architect\", it's often too late because there are already too many violations.   \nA better way is to define coding rules in JUnit tests. \nThis way, they are asserted automatically and regularly. \nViolations of rules break the build and therefore, one is forced to either \nadjust the code to comply with the rules or to adapt the rules in a reasonable way.\n\ncode-assert supports rules on the package structure and the test coverage.\nIt also integrates several static code analysis tools.\n\n## Language independent checks\n- [Dependency](#user-content-dependency)\n- [Test coverage](#user-content-test-coverage)\n- [FindBugs](#user-content-findbugs)\n\n## Java checks\n- [Checkstyle](#user-content-checkstyle)\n- [PMD](#user-content-pmd)\n\n## Kotlin checks\n- [ktlint](#user-content-ktlint)\n- [detekt](#user-content-detekt)\n\n## Other\n- [Configuration reuse](#user-content-configuration-reuse)\n- [Standard tests](#user-content-standard-tests)\n\n***\n\n### Dependency\n\nThis is based on code from [JDepend](https://github.com/clarkware/jdepend).\nIt checks if the package structure contains cycles and/or follows the defined rules.\n\n[//]: # (dependency)\n```java\npublic class DependencyTest {\n\n    // Analyze all sources in src/main/java\n    private final AnalyzerConfig config = AnalyzerConfig.maven().main();\n\n    @Test\n    public void noCycles() {\n        assertThat(new DependencyAnalyzer(config).analyze(), hasNoCycles());\n    }\n\n    @Test\n    public void dependency() {\n        // Defines the dependency rules for package org.proj\n        class OrgProj extends DependencyRuler {\n            // Rules for org.proj.dep, org.proj.model, org.proj.util\n            DependencyRule dep, model, util;\n\n            @Override\n            public void defineRules() {\n                base().mayUse(util, dep.allSubOf()); //org.proj may use org.proj.util and all subpackages of org.proj.dep\n                dep.andAllSub().mustUse(model); //org.proj.dep and all subpackages thereof must use org.proj.model\n                model.mayUse(util).mustNotUse(base()); //org.proj.model may use org.proj.util but not org.proj\n            }\n        }\n\n        // All dependencies are forbidden, except the ones defined in OrgProj\n        // java, org, net packages may be used freely\n        DependencyRules rules = DependencyRules.denyAll()\n                .withRelativeRules(new OrgProj())\n                .withExternals(\"java.*\", \"org.*\", \"net.*\");\n\n        DependencyResult result = new DependencyAnalyzer(config).rules(rules).analyze();\n        assertThat(result, matchesRulesExactly());\n    }\n}\n```\n[//]: # (end)\n\n### Test coverage\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/guru.nidi/code-assert-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/guru.nidi/code-assert-maven-plugin)\n\n\nTo verify the test coverage of a project, [JaCoCo](http://eclemma.org/jacoco/trunk/index.html) can be used.\nThe following steps are needed:\n* Add this to the `\u003cbuild\u003e\u003cplugins\u003e` section of `pom.xml`:\n```xml\n\u003cplugin\u003e\n    \u003cgroupId\u003eguru.nidi\u003c/groupId\u003e\n    \u003cartifactId\u003ecode-assert-maven-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e0.9.14\u003c/version\u003e\n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cgoals\u003e\n                \u003cgoal\u003eprepare\u003c/goal\u003e\n                \u003cgoal\u003eassert\u003c/goal\u003e\n            \u003c/goals\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e\n```            \n* `prepare` sets up the surefire plugin to run the tests with the JaCoCo agent which collects coverage data.\n* `assert` generates a coverage report and runs a coverage test\n    (default is `src/test/java/CodeCoverage.java`, configurable through the `testClass` property).\n* Write a code coverage test:\n\n[//]: # (codeCoverage)\n```java\npublic class CodeCoverage {\n    @Test\n    public void coverage() {\n        // Coverage of branches must be at least 70%, lines 80% and methods 90%\n        // This is checked globally and for all packages except for entities.\n        JacocoAnalyzer analyzer = new JacocoAnalyzer(new CoverageCollector(BRANCH, LINE, METHOD)\n                .just(For.global().setMinima(70, 80, 90))\n                .just(For.allPackages().setMinima(70, 80, 90))\n                .just(For.thePackage(\"org.proj.entity.*\").setNoMinima()));\n        assertThat(analyzer.analyze(), hasEnoughCoverage());\n    }\n}\n```\n[//]: # (end)\n\n### FindBugs\n\nRuns [FindBugs](http://findbugs.sourceforge.net/) on the code and finds questionable constructs.\n\n[//]: # (findBugs)\n```java\npublic class FindBugsTest {\n    @Test\n    public void findBugs() {\n        // Analyze all sources in src/main/java\n        AnalyzerConfig config = AnalyzerConfig.maven().main();\n\n        // Only treat bugs with rank \u003c 17 and with NORMAL_PRIORITY or higher\n        // Ignore the given bug types in the given classes / methods.\n        BugCollector collector = new BugCollector().maxRank(17).minPriority(Priorities.NORMAL_PRIORITY)\n                .just(In.everywhere().ignore(\"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR\"))\n                .because(\"It's checked and OK like this\",\n                        In.classes(DependencyRules.class, PmdRuleset.class).ignore(\"DP_DO_INSIDE_DO_PRIVILEGED\"),\n                        In.classes(\"*Test\", \"Rulesets\")\n                                .and(In.classes(\"ClassFileParser\").withMethods(\"doParse\"))\n                                .ignore(\"URF_UNREAD_FIELD\"));\n\n        FindBugsResult result = new FindBugsAnalyzer(config, collector).analyze();\n        assertThat(result, hasNoBugs());\n    }\n}\n```\n[//]: # (end)\n\n### Checkstyle\n\nRuns [checkstyle](http://checkstyle.sourceforge.net/) on the code and finds questionable constructs.\n\n[//]: # (checkstyle)\n```java\npublic class CheckstyleTest {\n    @Test\n    public void checkstyle() {\n        // Analyze all sources in src/main/java\n        AnalyzerConfig config = AnalyzerConfig.maven().main();\n\n        // Only treat issues with severity WARNING or higher\n        StyleEventCollector collector = new StyleEventCollector().severity(SeverityLevel.WARNING)\n                .just(In.everywhere().ignore(\"import.avoidStar\", \"javadoc.missing\"))\n                .because(\"in tests, long lines are ok\", In.classes(\"*Test\").ignore(\"maxLineLen\"));\n\n        //use google checks, but adjust max line length\n        final StyleChecks checks = StyleChecks.google().maxLineLen(120);\n\n        CheckstyleResult result = new CheckstyleAnalyzer(config, checks, collector).analyze();\n        assertThat(result, hasNoCheckstyleIssues());\n    }\n}\n```\n[//]: # (end)\n\n### PMD\n\nRuns [PMD](https://pmd.github.io/) on the code and finds questionable constructs and code duplications.\n\n[//]: # (pmd)\n```java\npublic class PmdTest {\n\n    // Analyze all sources in src/main/java\n    private final AnalyzerConfig config = AnalyzerConfig.maven().main();\n\n    @Test\n    public void pmd() {\n        // Only treat violations with MEDIUM priority or higher\n        // Ignore the given violations in the given classes / methods\n        PmdViolationCollector collector = new PmdViolationCollector().minPriority(RulePriority.MEDIUM)\n                .because(\"It's not severe and occurs very often\",\n                        In.everywhere().ignore(\"MethodArgumentCouldBeFinal\"),\n                        In.locs(\"JavaClassBuilder#from\", \"FindBugsMatchers\").ignore(\"AvoidInstantiatingObjectsInLoops\"))\n                .because(\"it'a an enum\",\n                        In.classes(\"SignatureParser\").ignore(\"SwitchStmtsShouldHaveDefault\"))\n                .just(In.classes(\"*Test\").ignore(\"TooManyStaticImports\"));\n\n        // Define and configure the rule sets to be used\n        PmdAnalyzer analyzer = new PmdAnalyzer(config, collector).withRulesets(\n                basic(), braces(), design(), empty(), optimizations(),\n                codesize().excessiveMethodLength(40).tooManyMethods(30));\n\n        assertThat(analyzer.analyze(), hasNoPmdViolations());\n    }\n\n    @Test\n    public void cpd() {\n        // Ignore duplications in the given classes\n        CpdMatchCollector collector = new CpdMatchCollector()\n                .because(\"equals\",\n                        In.everywhere().ignore(\"public boolean equals(Object o) {\"))\n                .just(\n                        In.classes(DependencyRule.class, Dependencies.class).ignoreAll(),\n                        In.classes(\"SignatureParser\").ignoreAll());\n\n        // Only treat duplications with at least 20 tokens\n        CpdAnalyzer analyzer = new CpdAnalyzer(config, 20, collector);\n\n        assertThat(analyzer.analyze(), hasNoCodeDuplications());\n    }\n}\n```\n[//]: # (end)\n\n### ktlint\n\nRuns [ktlint](https://ktlint.github.io/), a kotlin linter.\n\n[//]: # (ktlint)\n```java\npublic class KtlintTest {\n    @Test\n    public void analyze() {\n        // Analyze all sources in src/main/kotlin\n        AnalyzerConfig config = AnalyzerConfig.maven(KOTLIN).main();\n\n        KtlintCollector collector = new KtlintCollector()\n                .just(In.classes(\"Linker\").ignore(\"no-semi\"));\n\n        KtlintResult result = new KtlintAnalyzer(config, collector).analyze();\n\n        assertThat(result, hasNoKtlintIssues());\n    }\n}\n```\n[//]: # (end)\n\n### detekt\n\nRuns [detekt](https://github.com/arturbosch/detekt), a static code analysis tool for kotlin.\n\n[//]: # (detekt)\n```java\npublic class DetektTest {\n    @Test\n    public void analyze() {\n        // Analyze all sources in src/main/kotlin\n        AnalyzerConfig config = AnalyzerConfig.maven(KOTLIN).main();\n\n        DetektCollector collector = new DetektCollector()\n                .just(In.classes(\"Linker\").ignore(\"MaxLineLength\"));\n\n        DetektResult result = new DetektAnalyzer(config, collector).analyze();\n\n        assertThat(result, hasNoDetektIssues());\n    }\n}\n```\n[//]: # (end)\n\n### Configuration reuse\n\nCollector configurations can be defined separately and thus reused.\nSome configurations are defined in [PredefConfig](code-assert/src/main/java/guru/nidi/codeassert/junit/PredefConfig.java).\n\n[//]: # (reuse)\n```java\nprivate final CollectorTemplate\u003cIgnore\u003e pmdTestCollector = CollectorTemplate.forA(PmdViolationCollector.class)\n        .because(\"It's a test\", In.classes(\"*Test\")\n                .ignore(\"JUnitSpelling\", \"AvoidDuplicateLiterals\", \"SignatureDeclareThrowsException\"))\n        .because(\"It's compiler generated code\", In.languages(KOTLIN)\n                .ignore(\"BC_BAD_CAST_TO_ABSTRACT_COLLECTION\"));\n\n@Test\npublic void pmd() {\n    PmdViolationCollector collector = new PmdViolationCollector().minPriority(RulePriority.MEDIUM)\n            .apply(pmdTestCollector)\n            .because(\"It's not severe and occurs often\", In.everywhere().ignore(\"MethodArgumentCouldBeFinal\"));\n\n    PmdAnalyzer analyzer = new PmdAnalyzer(config, collector).withRulesets(rules);\n    assertThat(analyzer.analyze(), hasNoPmdViolations());\n}\n```\n[//]: # (end)\n\n\n### Standard tests\n\nA test can inherit from `CodeAssertTest`. It should override one or more `analyzeXXX` methods.\nIf it does so, these standard checks will be executed:\n* dependency rules\n* circular dependencies\n* PMD\n* PMD - unused actions\n* CPD\n* CPD - unused actions\n* FindBugs\n* FindBugs - unused actions\n* Checkstyle\n* Checkstyle - unused actions\n\n[//]: # (codeTest)\n```java\n//extend CodeAssertTest if you still use JUnit 4\npublic class CodeTest extends CodeAssertJunit5Test {\n\n    private static final AnalyzerConfig CONFIG = AnalyzerConfig.maven().main();\n\n    @Override\n    protected DependencyResult analyzeDependencies() {\n        class MyProject extends DependencyRuler {\n            DependencyRule packages;\n\n            @Override\n            public void defineRules() {\n                //TODO\n            }\n        }\n\n        final DependencyRules rules = denyAll().withExternals(\"java.*\").withRelativeRules(new MyProject());\n        return new DependencyAnalyzer(CONFIG).rules(rules).analyze();\n    }\n\n    @Override\n    protected FindBugsResult analyzeFindBugs() {\n        final BugCollector bugCollector = new BugCollector().just(\n                In.classes(\"*Exception\").ignore(\"SE_BAD_FIELD\"));\n        return new FindBugsAnalyzer(CONFIG, bugCollector).analyze();\n    }\n\n    @Override\n    protected CheckstyleResult analyzeCheckstyle() {\n        final StyleEventCollector bugCollector = new StyleEventCollector().just(\n                In.everywhere().ignore(\"javadoc.missing\"));\n        return new CheckstyleAnalyzer(CONFIG, StyleChecks.google(), bugCollector).analyze();\n    }\n\n    @Override\n    protected PmdResult analyzePmd() {\n        final PmdViolationCollector collector = new PmdViolationCollector().just(\n                In.everywhere().ignore(\"MethodArgumentCouldBeFinal\"));\n        return new PmdAnalyzer(CONFIG, collector).withRulesets(basic(), braces()).analyze();\n    }\n}\n```\n[//]: # (end)\n\n\n","funding_links":["https://github.com/sponsors/nidi3"],"categories":["静态分析"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnidi3%2Fcode-assert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnidi3%2Fcode-assert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnidi3%2Fcode-assert/lists"}