Git Product home page Git Product logo

archunit's Introduction

CI Maven Central License

ArchUnit

ArchUnit is a free, simple and extensible library for checking the architecture of your Java code. That is, ArchUnit can check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more. It does so by analyzing given Java bytecode, importing all classes into a Java code structure. ArchUnit's main focus is to automatically test architecture and coding rules, using any plain Java unit testing framework.

An Example

Add the Maven Central dependency to your project

Gradle
testImplementation 'com.tngtech.archunit:archunit:1.3.0'
Maven
<dependency>
    <groupId>com.tngtech.archunit</groupId>
    <artifactId>archunit</artifactId>
    <version>1.3.0</version>
    <scope>test</scope>
</dependency>

Create a test

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;

public class MyArchitectureTest {
    @Test
    public void some_architecture_rule() {
        JavaClasses importedClasses = new ClassFileImporter().importPackages("com.myapp");
    
        ArchRule rule = classes()... // see next section
    
        rule.check(importedClasses);
    }
}

Let the API guide you

ArchUnit Fluent API

Where to look next

For further information, check out the user guide at http://archunit.org or test examples for the current release at ArchUnit Examples.

License

ArchUnit is published under the Apache License 2.0, see http://www.apache.org/licenses/LICENSE-2.0 for details.

It redistributes some third party libraries:

All licenses for ArchUnit and redistributed libraries can be found within the licenses folder.

archunit's People

Contributors

aaschmid avatar actions-user avatar bedla avatar codecholeric avatar dependabot[bot] avatar eddumelendez avatar farbauti89 avatar gernotstarke avatar giorgadzeluka avatar goetzd avatar gradle-update-robot avatar hankem avatar jjank avatar korsin avatar leonardhusmann avatar marknp avatar mikomatic avatar mobolajiadefope-toast avatar oberprah avatar perlun avatar pfoerd avatar roxspring avatar rweisleder avatar spanierm42 avatar stefluhh avatar sullis avatar tfij avatar thmuch avatar vincent-fuchs avatar wolfs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

archunit's Issues

[FeatureRequest] let me specify which type of transation my class requires

Wrote Test

@ArchTest
public static void classIsInRepositoryPackageSoItsAnnotatedWithTransactional(JavaClasses classes) {
    classes()
        .that().resideInAPackage("..repository..")
        .should().beAnnotatedWith(Transactional.class)
        .check(classes);
}

So now no class is allowed in repository package that does not have a Transactional annotation.
It would be nice to be able to specify which type of org.springframework.transaction.annotation.Propagation is required.

The rule API should not only offer checks for accesses, but also for more generic dependencies in any way

The most typical violations of components / layers can be caught by using a rule like

noClasses().that()...should().accessClassesThat()...

However, there are different types of dependencies, that are not caught by this (annotations, declared fields that are never accessed, etc.). While it is possible to check for this with several rules, the rule API should offer an easy and extensive way to check for this. Proposal:

noClasses().that()...should().dependOnClassesThat()...

And likewise, it should be possible to check the other way around

classes().that()...should().onlyHaveDepedentClassesThat()...

I.e. the last rule checks that all classes depending on this class should satisfy some condition.

LayeredArchitecture should allow exceptions

In any legacy project trying to introduce checks for layer dependencies will most likely result in a lot of existing violations.
It should be possible to ignore existing violations in a convenient way.
Suggestion:

layeredArchitecture()
    ...
    .whereLayer("foo").mayOnlyBeAccessedByLayers("bar")
    .ignore(/*(Class/ClassName/DescribedPredicate)*/ from, (/*(Class/ClassName/DescribedPredicate)*/ to)
    .ignore(...);

jekyll/docker has github authentication error when starting jekyll server

actually, this is more of a pull-request than issue, but:
too bad, I cannot open a pull request on the orphaned gh-pages branch, therefore I attach the modified Gemfile here:

source "https://rubygems.org"

# commented the line below to get rid of github authentication warnings
# gem "github-pages", group: :jekyll_plugins

# added the following line, same reason as above
gem "jekyll", group: :jekyll_plugins 

group :jekyll_plugins do
  gem "jekyll-paginate"
  gem "jekyll-sitemap"
  gem "jekyll-gist"
  gem "jekyll-feed"
  gem "jemoji"

end

SecurityTest fails with mac JVM

The SecurityTest tests if java.security.cert is accessed only by certain packages. On Mac jvms apple.security is also accessing the cert package.

Improve reporting of ignored violations

Typically in legacy code bases there will be a ton of violations, when architecture rules are introduced for the first time. ArchUnit supports the 'archunit_ignore_patterns.txt' file, to exclude messages based on regular expressions, but once violations are ignored, there is no further feedback.
The following is a list of suggestions, to improve the overview:

  • Log a message for each ignored violation, so the process is more transparent and it's easier to trace why a violation isn't reported. I would propose INFO level, because it's neither a warning per se (after all, it was configured that way and behaves as expected), nor is it some mainly unimportant information from a business point of view that you only want on DEBUG. Any objections?
  • Improve priorities -> make it configurable, if a priority should already cause a rule to fail, or just log the message (e.g. archunit.properties>failTestsOfPriority=MEDIUM)
  • Add a full html report for the current state, including passed, violated and ignored rules. This would be the biggest part, and I wonder if it should be its own module, instead of being part of the core functionality. (my guts tell me yes, since it doesn't feel like ArchUnit's core domain, but an extension)

About us

Hey there,
I'd need to find out, still not been able to, if it's possibile to prevent a class to use an annotation with a given value of one of its attributes.
for instance, to enforce a rule which prevents the value EAGER like

fetch = FetchType.EAGER

to be used in the annotation javax.persistence.OneToMany

thanks in advance,
s2r

Issue a warning if we check an annotation with RetentionPolicy.SOURCE

When executing an ArchRule like noClasses().should().beAnnotatedWith(SuppressWarnings.class)ArchUnit reports no violations, even if there are SuppressWarnings annotations on some of the classes.

The reason is that SuppressWarnings has RetentionPolicy.SOURCE, i.e. ArchUnit cannot query these annotations.

Would it be possible for ArchUnit to issue some warning (or better: fail the test?) if we're checking a SOURCE annotation?

Java 9 support

With Java 9, the application class loader isn't an instance of URLClassLoader any more, but ArchUnit searches for a class loader of this type.
So ArchUnit doesn't find any classes in class path.
So no classes are checked and all tests pass successfully.
ArchUnit must be modified to support both Java 8 or lower and Java 9 class loading mechanism.

How to restrict the using of some of library in a class ?

Hi Peter, I very like the idea of the ArchUnit.

My question is it possible to create a rule that does not allow to use some third-party libraries?

For instance, there are several libraries for test assertions, but I want a team use only one in test classes.

From my point of view, I can simply check imports of test classes and raise a fail if import in a "blacklist".

Class file import should be resilient against broken class files

Currently one broken file evil.class breaks the whole import causing an exception. It would be more convenient, to just log a warning and move on.
How to reproduce:

echo "broken" > /some/test/folder/Evil.class
cp some.GoodClass.class /some/test/folder

then

JavaClasses classes = new ClassFileImporter().importPath(Paths.get("/some/test/folder"));

Expected: some.GoodClass to be imported
Actual: Import dies with an exception

isEnum method for JavaClass

Hi @codecholeric,

Thank you for this great library. It is awesome and saves me a lot of time.
Recently I've faced a case where i wanted to check that JavaClass is enum.
Currently I can solve this by: javaClass.reflect().isEnum(), but it would be more convenient to have such check in JavaClass.

If you don't mind I'll create corresponding PR.

Exclude self from violations of onlyAccessedBy(..) by default

When checking

classes()...should().onlyBeAccessedBy()...

it would make sense to ignore violations by self access by default. Often usecases look like

classes().that().resideInAPackage("..bar..").should().onlyBeAccessedByAnyPackage("..foo..", "..bar..")

because otherwise a ton of violations of '..bar..' calling itself would be reported.
It seems to never make sense, to report self-accesses, so this should be excluded by default, making rules easier to specify.

Package dependency check with enumerations

At the company we work, we want to isolate a package gradually to reach a point where it can be taken to its own JAR. That's why we want to avoid this package to access others in the project in the meantime and I want to use ArchUnit to force this, but I have a problem with the enumerations within the package.

Maybe I'm doing it wrong, or there is a workaround for this. I appreciate any light that takes me in the right path.

I prepared a demo that replicates the behavior here: https://github.com/paolocarrasco/archunit-enumeration-issue.

The body of my test goes something like this:

		noClasses()
			.that()
			.resideInAPackage("package1")
			.should()
			.accessClassesThat()
			.resideOutsideOfPackages(
					"java..",
					"..package1..",
					"..package2..")
			.check(this.javaClasses);

After I run the test it shows a message that states something similar to this:

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a package 'package1' should access classes that reside outside of packages ['java..', '..package1..', '..package2..']' was violated (1 times):
Method <package1.Colors.values()> calls method <[Lpackage1.Colors;.clone()> in (Colors.java:3)

	at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:92)
	at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
	at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:190)
	at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:75)
	at com.tngtech.archunit.lang.syntax.ClassesShouldThatInternal.check(ClassesShouldThatInternal.java:366)
	at DependenciesTest.shouldHaveDependenciesOnlyToLowerPackages(DependenciesTest.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

No hyperlinks if whole class violates a rule

The following rule produces an assertion error (in IntelliJ) that contains only text, no hyperlinks to the classes.

    /**
     * Test classes should not be public (Junit 5).
     */
    @Test
    void shouldNotUsePublicInTestCases() {
        JavaClasses classes = new ClassFileImporter().importPackages("edu.hm.hafner");

        ArchRule noPublicClasses = noClasses().that().haveSimpleNameEndingWith("Test")
                .should().bePublic();

        noPublicClasses.check(classes);
    }

Produces the output (example):

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that have simple name ending with 'Test' should be public' was violated (71 times):
class edu.hm.hafner.ArchitectureRulesTest has modifier PUBLIC
class edu.hm.hafner.analysis.AbstractParserTest has modifier PUBLIC
class edu.hm.hafner.analysis.IssueFilterTest has modifier PUBLIC
class edu.hm.hafner.analysis.IssueTest has modifier PUBLIC
class edu.hm.hafner.analysis.parser.AcuCobolParserTest has modifier PUBLIC
class edu.hm.hafner.analysis.parser.AjcParserTest has modifier PUBLIC
class edu.hm.hafner.analysis.parser.AnsibleLintTest has modifier PUBLIC
[...]

Expected result:
each class has a hyperlink (as the other assertion errors from ArchUnit).

The Cache used by `ArchUnitRunner` should allow some option to be cleaned after the test run

At the moment imported classes for a specific set of URLs will stay in memory forever. While this might be useful, if multiple tests in fact want to import the same set of classes, it might be necessary in some scenarios, to empty the cache after each test class is finished.
Proposal: It should be possible to declare

@AnalyzeClasses(cache = CacheMode.FOR_CLASS)

where the default is the current behavior CacheMode.FOREVER.

Option for clearing classes cache after all ArchTests in a test class have run

We're running some ArchTests using the performance optimized version with the AnalyzeClasses annotation:

@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "de.whatever")
public class ArchUnitTest {
   @ArchTest
   public static final ArchRule SOME_RULE = ...;
}

It seems the JavaClasses cache is not freed after running the tests in this class. (We're running into memory problems in the unit tests that are executed after ArchUnit. For performance reasons our tests use the same JVM instance.)

Is there an option for clearing the cache after all ArchTests in a test class have run?

Support for UML Diagrams

it would be neat to support a dependencies.uml.
this file should define the dependencies of the packages.
it could contain "unintented dependencies", but shouldn't.
a project then just should include archunit.jar, and then everybody could be sure, that dependencies.uml reflect the current project.
this way your UML keeps up to date and your depencies are better readable then a fluent api ever could be.

Tests fail on Windows because of illegal file URIs

I forked the repository and executed a build with clean build -PallTests. Without any changes the following tests fail on Windows because of illegal file URIs.

:archunit:test

com.tngtech.archunit.core.importer.ClassFileImporterSlowTest > imports_jars FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 5: file:\Users\xxxx\.gradle\caches\modules-2\files-2.1\junit\junit\4.12\2973d150c0dc1fefe998f834810d68f278ea58ec\junit-4.12.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJars(ClassFileImporter.java:112)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJars(ClassFileImporter.java:105)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJar(ClassFileImporter.java:100)
        at com.tngtech.archunit.core.importer.ClassFileImporterSlowTest.imports_jars(ClassFileImporterSlowTest.java:60)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 5: file:\Users\xxxx\.gradle\caches\modules-2\files-2.1\junit\junit\4.12\2973d150c0dc1fefe998f834810d68f278ea58ec\junit-4.12.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 6 more

com.tngtech.archunit.core.importer.ClassFileImporterTest > ImportOptions_are_respected FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 5: file:\Users\xxxx\.gradle\caches\modules-2\files-2.1\junit\junit\4.12\2973d150c0dc1fefe998f834810d68f278ea58ec\junit-4.12.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJars(ClassFileImporter.java:112)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJars(ClassFileImporter.java:105)
        at com.tngtech.archunit.core.importer.ClassFileImporter.importJar(ClassFileImporter.java:100)
        at com.tngtech.archunit.core.importer.ClassFileImporterTest.ImportOptions_are_respected(ClassFileImporterTest.java:1757)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 5: file:\Users\xxxx\.gradle\caches\modules-2\files-2.1\junit\junit\4.12\2973d150c0dc1fefe998f834810d68f278ea58ec\junit-4.12.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 6 more

com.tngtech.archunit.core.importer.ClassFileSourceTest > classes_in_JAR_are_filtered[0: [/one/Foo.class, /one/Bar.class, /two/Bar.class], com.tngtech.archunit.core.importer.ImportOptions@22bf94fb, [/one/Foo.class, /one/Bar.class, /two/Bar.class]] FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878364045828424399998574865074908\test.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileSourceTest.classes_in_JAR_are_filtered(ClassFileSourceTest.java:62)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878364045828424399998574865074908\test.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 3 more

com.tngtech.archunit.core.importer.ClassFileSourceTest > classes_in_JAR_are_filtered[1: [/one/Foo.class, /one/Bar.class, /two/Bar.class], com.tngtech.archunit.core.importer.ImportOptions@1ae7f181, [/one/Foo.class, /one/Bar.class]] FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878372910508525595324178910651973\test.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileSourceTest.classes_in_JAR_are_filtered(ClassFileSourceTest.java:62)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878372910508525595324178910651973\test.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 3 more

com.tngtech.archunit.core.importer.ClassFileSourceTest > classes_in_JAR_are_filtered[2: [/one/Foo.class, /one/Bar.class, /two/Bar.class], com.tngtech.archunit.core.importer.ImportOptions@7de272a, [/two/Bar.class]] FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp987837304491962-8304586973681892963\test.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileSourceTest.classes_in_JAR_are_filtered(ClassFileSourceTest.java:62)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp987837304491962-8304586973681892963\test.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 3 more

com.tngtech.archunit.core.importer.ClassFileSourceTest > classes_in_JAR_are_filtered[3: [/one/Foo.class, /one/Bar.class, /two/Bar.class], com.tngtech.archunit.core.importer.ImportOptions@732cfbe1, [/one/Bar.class, /two/Bar.class]] FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878373187069975548690689213990652\test.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileSourceTest.classes_in_JAR_are_filtered(ClassFileSourceTest.java:62)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878373187069975548690689213990652\test.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 3 more

com.tngtech.archunit.core.importer.ClassFileSourceTest > classes_in_JAR_are_filtered[4: [/one/Foo.class, /one/Bar.class, /two/Bar.class], com.tngtech.archunit.core.importer.ImportOptions@2deaa133, []] FAILED
    java.lang.IllegalArgumentException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878373309837588610823013150992030\test.jar
        at java.net.URI.create(URI.java:852)
        at com.tngtech.archunit.core.importer.Location.newFileUri(Location.java:164)
        at com.tngtech.archunit.core.importer.Location.of(Location.java:143)
        at com.tngtech.archunit.core.importer.ClassFileSourceTest.classes_in_JAR_are_filtered(ClassFileSourceTest.java:62)

        Caused by:
        java.net.URISyntaxException: Illegal character in opaque part at index 7: file:C:\Users\xxxx\AppData\Local\Temp\archtmp9878373309837588610823013150992030\test.jar
            at java.net.URI$Parser.fail(URI.java:2848)
            at java.net.URI$Parser.checkChars(URI.java:3021)
            at java.net.URI$Parser.parse(URI.java:3058)
            at java.net.URI.<init>(URI.java:588)
            at java.net.URI.create(URI.java:850)
            ... 3 more

com.tngtech.archunit.core.importer.LocationTest > initializationError FAILED
    java.lang.Error: Cannot explode 'LocationTest.JAR_protocol_is_added_to_file_urls_that_point_to_JARs' using 'file_locations_pointing_to_jar' due to: Exception while invoking dataprovider method 'file_locations_pointing_to_jar': Illegal character in authority at index 7: file://C:\Users\xxxx\AppData\Local\Temp\archtmp9878411977992676820986918056713005\test.jar

        Caused by:
        java.lang.IllegalArgumentException: Exception while invoking dataprovider method 'file_locations_pointing_to_jar': Illegal character in authority at index 7: file://C:\Users\xxxx\AppData\Local\Temp\archtmp9878411977992676820986918056713005\test.jar

            Caused by:
            java.lang.IllegalArgumentException: Illegal character in authority at index 7: file://C:\Users\xxxx\AppData\Local\Temp\archtmp9878411977992676820986918056713005\test.jar
                at java.net.URI.create(URI.java:852)
                at com.tngtech.archunit.core.importer.LocationTest.file_locations_pointing_to_jar(LocationTest.java:64)

                Caused by:
                java.net.URISyntaxException: Illegal character in authority at index 7: file://C:\Users\xxxx\AppData\Local\Temp\archtmp9878411977992676820986918056713005\test.jar
                    at java.net.URI$Parser.fail(URI.java:2848)
                    at java.net.URI$Parser.parseAuthority(URI.java:3186)
                    at java.net.URI$Parser.parseHierarchical(URI.java:3097)
                    at java.net.URI$Parser.parse(URI.java:3053)
                    at java.net.URI.<init>(URI.java:588)
                    at java.net.URI.create(URI.java:850)
                    ... 1 more

2945 tests completed, 8 failed
:archunit:test FAILED

onlyBeAccessed() with more then one condition?

Is there a possibility to specify two or more conditions for the onlyBeAccessed() call?

For example:

classes().that().areAnnotatedWith(Service.class)
            .should().onlyBeAccessed().byClassesThat().areAnnotatedWith(Controller.class)
            .orShould().onlyBeAccessed().byClassesThat().areAnnotatedWith(RestController.class);

The API should support testing for "equivalence" to classes

Sometimes a rule only targets a single class (e.g. class Foo is only accessed by classes matching a certain pattern). It should be possible to supply the class object in that case, instead of writing classes().that().haveFullyQualifiedName(Foo.class.getName()). Thus I propose an API extension:

  • classes().that().areEquivalentTo(Foo.class)
  • classes().should().beEquivalentTo(Foo.class)

which just pipes to JavaClass.isEquivalentTo(Foo.class).

Question about onlyBeAccessed()

Hi,

I stumbled upon this amazing framework when I read the article on informatik-aktuell.de. The simple idea of this amazes me, because for our Microservices Domain Driven Design project I see a big potential to make use of it, since I am faced often with exactly these questions like "Why can I not directly access the ValueObject out from the ApplicationService" and so on.

So right now I am experimenting a bit and I am trying to setup this enforcement:

@ArchTest
public static final ArchRule enforceValueObjectsCanOnlyBeAccessFromOtherVOAndEntities =
        classes()
                .that().areAssignableTo(ValueObject.class)
                .should().onlyBeAccessed().byClassesThat().areAssignableTo(ValueObject.class)
                .orShould().onlyBeAccessed().byClassesThat().areAssignableTo(Entity.class);

However I seem to miss something, because running this code results in a lot of violations, where it even (!) says something like this:

Method <...Birthdate.hasAgeRequirement(int)> gets field <...Birthdate.birthdate> in (Birthdate.java:39)

How can this be? I mean obviously Birthdate was identified as an ValueObject (which it is), but why is it not allowed to access fields of itself?

Any help is appreciated.

Stef

Check multiple ArchRules at once

I want to provide a shared library with common rules. These rules are used in different corporate projects. This is to ensure that every project is built the same way.
Example:

@Test
void checkCommonRules() {
    GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS.check(javaClasses);
    GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS.check(javaClasses);
    GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.check(javaClasses);
}

This prints only the violations of the first violated rule.

Now I want to ensure that in every project every of those common rules is checked. If a new common rule is added to the library, every project should execute this new rule without further code modification.

I thought of something like this:

public ArchRules COMMON_RULES = ArchRules.of(
    GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS,
    GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS,
    GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING
);
@Test
void checkCommonRules() {
    GeneralCodingRules.COMMON_RULES.check(javaClasses);
}

This check should print all violations of all given rules.

Handle Meta-Annotations

The Spring Framework allows (and encourages) to compose annotations, for example composing @Service and @Transactional to @TransactionalService

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional
public @interface TransactionalService {
}

@TransactionalService
public class DummyBean {    
}

With ArchUnit it should be possible to access the meta-annotations @Service and @Transactional, for example

@Test
void serviceAnnotation() {
    JavaClasses classes = new ClassFileImporter().importPackagesOf(DummyBean.class);

    classes()
            .that().areAssignableTo(DummyBean.class)
            .should().beAnnotatedWith(Service.class)
            .check(classes);
}

With 0.5.0 this test fails with

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'classes that are assignable to example.DummyBean should be annotated with @Service' was violated (1 times):
class example.DummyBean is not annotated with @Service

	at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:92)
	at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
	at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:190)
	at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:75)
	at example.DummyTest.serviceAnnotation(DummyTest.java:61)

See also the Spring Annotation Programming Model.

How to relax the rules for tests

ArchUnit is a great testing framework! I wonder whether there is a good way to relax the rules for classes under /src/test/java in a portable way.

Enhance @AnalyzeClasses

While filtering imports via importOptions= is quite powerful, choosing the source of the classes is not (you can only choose the whole classpath, packages, or classes defining packages).

We should extend @AnalyzeClasses, to also allow specifying some generic "UrlProvider" type (or similar), that can in turn provide arbitrary URLs to import classes from.

Visualization

I just wrote my first test with ArchUnit. I really like your DSL, much nicer than what I created with Degraph https://github.com/schauder/degraph

There is just one thing that I miss from Degraph: the ability to create visual representations of dependencies, especially in the case of failures.

Would you be interested in adding a feature that would cause a (failing) test to create a graphml file containing a diagram similar to what Degraph creates? Like this: http://blog.schauderhaft.de/degraph/documentation.html#result

Don't if I find the time, but I would be interested in creating a PR.

Should-Syntax should contain 'interface' handling

While there is a predicate syntax

classes().that().implement(foo)

the only way to filter for interfaces is using

classes().that(are(JavaClass.Predicates.INTERFACES))

The fluent API should allow filtering for interfaces

classes().that().areInterfaces()
classes().that().areNoInterfaces()

as well as asserting interfaces

classes().should().beInterfaces()
classes().should().notBeInterfaces()

Assertions for return parameters

I'd like to implement tests with ArchUnit that basically assert the following:

Public methods of classes in package ABC always return classes of a certain type

In my particular case, I'd like to make sure that public methods of classes in the controller layer always return a particular class that we use as a wrapper for API responses.

Other scenarios I can think of (and would want to implement in the future) could be to check that public methods of classes of the service layer always return classes (or collections of classes) of the domain layer.

I've been checking the ArchUnit-Examples and the User Guide but couldn't find any APIs related to method return values. I also checked the current issues and didn't find anything related to this.

Is that something that you'd see in scope for ArchUnit?

Expose line number of the definition of a JavaMember

For a custom ArchCondition that checks the definition of a JavaClass/JavaMethod/JavaField it would be useful to append the declaration line to the error message as clickable link.

Example:

all(javaMethods).should(haveNameStartingWithLowerCase()).check(javaClasses);

private ArchCondition<JavaMethod> haveNameStartingWithLowerCase() {
    return new ArchCondition<JavaMethod>("have name starting with lower case") {
        @Override
        public void check(JavaMethod item, ConditionEvents events) {
            boolean satisfied = Character.isLowerCase(item.getName().charAt(0));
            String message = item.getFullName() + " " + Formatters.formatLocation(item.getOwner(), item.getDeclarationLineNumber());
            events.add(new SimpleConditionEvent(item, satisfied, message));
        }
    };
}

would print

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'methods should have name starting with lower case' was violated (1 times):
demo.GreetEndpoint.Greet(java.lang.String) (GreetEndpoint.java:23)

Relates to #38

Warning MalformedURLException when executing tests

In the console log I see multiple MalformedURLException:

10:32:08.043 [main] WARN com.tngtech.archunit.core.importer.UrlSource$From - Cannot parse URL from path C:\Program Files\Java\jdk1.8.0_112\jre\lib\resources.jar!/
java.net.MalformedURLException: invalid url: file://C:%5CProgram%20Files%5CJava%5Cjdk1.8.0_112%5Cjre%5Clib%5Cresources.jar!/ (java.net.MalformedURLException: For input string: "%5CProgram%20Files%5CJava%5Cjdk1.8.0_112%5Cjre%5Clib%5Cresources.jar")
	at java.net.URL.<init>(URL.java:627)
	at java.net.URL.<init>(URL.java:490)
	at java.net.URL.<init>(URL.java:439)
	at com.tngtech.archunit.core.importer.UrlSource$From.newUrl(UrlSource.java:100)
	at com.tngtech.archunit.core.importer.UrlSource$From.newJarUri(UrlSource.java:95)
	at com.tngtech.archunit.core.importer.UrlSource$From.parseClassPathEntry(UrlSource.java:86)
	at com.tngtech.archunit.core.importer.UrlSource$From.findUrlsForClassPathProperty(UrlSource.java:79)
	at com.tngtech.archunit.core.importer.UrlSource$From.classPathSystemProperties(UrlSource.java:70)
	at com.tngtech.archunit.core.importer.LocationResolver$Legacy.resolveClassPath(LocationResolver.java:38)
	at com.tngtech.archunit.core.importer.Locations.getLocationsOf(Locations.java:88)
	at com.tngtech.archunit.core.importer.Locations.ofPackage(Locations.java:63)
	at com.tngtech.archunit.junit.ClassCache.locationsOf(ClassCache.java:136)
	at com.tngtech.archunit.junit.ClassCache.getLocationsOfPackages(ClassCache.java:103)
	at com.tngtech.archunit.junit.ClassCache.locationsToImport(ClassCache.java:91)
	at com.tngtech.archunit.junit.ClassCache.getClassesToAnalyzeFor(ClassCache.java:74)
	at com.tngtech.archunit.junit.ArchUnitRunner.runChild(ArchUnitRunner.java:133)
	at com.tngtech.archunit.junit.ArchUnitRunner.runChild(ArchUnitRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at com.tngtech.archunit.junit.ArchUnitRunner$1.evaluate(ArchUnitRunner.java:73)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException: invalid url: file://C:%5CProgram%20Files%5CJava%5Cjdk1.8.0_112%5Cjre%5Clib%5Cresources.jar!/ (java.net.MalformedURLException: For input string: "%5CProgram%20Files%5CJava%5Cjdk1.8.0_112%5Cjre%5Clib%5Cresources.jar")
	at sun.net.www.protocol.jar.Handler.parseAbsoluteSpec(Handler.java:178)
	at sun.net.www.protocol.jar.Handler.parseURL(Handler.java:151)
	at java.net.URL.<init>(URL.java:622)
	... 28 common frames omitted

I'm running the tests on Windows.

Do these exception have any impact on the functionality of ArchUnit?

Btw. thanks a lot for this excellent tool!

Improve handling of JUnit support with regard to Kotlin

When Kotlin is used together with the JUnit support, writing rules becomes too bloated, since it's necessary to create a companion object due to the static requirements for fields.
I.e. at the moment ArchUnit JUnit tests in Kotlin have to be written like

@RunWith(ArchUnitRunner::class)
@AnalyzeClasses(packages = ["com.mycompany.myapp"])
class MyArchitectureTest {

    companion object {
        @ArchTest @JvmField val myRule = classes()
            .that().resideInAPackage("..service..")
            .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..")
    }
}

This seems unnecessary bloated, what we would want, is

@RunWith(ArchUnitRunner::class)
@AnalyzeClasses(packages = ["com.mycompany.myapp"])
class MyArchitectureTest {

     @ArchTest val myRule = classes()
            .that().resideInAPackage("..service..")
            .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..")

}

Detect all circular references

Is it possible to detect all cyclic references? I know the syntax for slices e.g.

slices().matching("com.hascode.(tutorial).(*)").namingSlices("$2 of $1").should()
          .beFreeOfCycles();

Is it possible somehow to write something similar to this one?

classes().should().beFreeOfCycles();

ClassFileImporter doesn't import packages, if respective Jar Entry is missing

The current implementation relies on ClassLoader#getResources("/some/pkg") to import package some.pkg. This works for most Jars, however, if the Jar doesn't contain an entry for the respective folder, getResources(..) will return an empty array, and thus classes won't be imported.

An easy way to reproduce this, is to import java.io and observe, that java.io.File will be missing. This happens, because the default rt.jar is missing the entry /java/io, but has an entry /java/io/File.class.

Obviously, this is not desired behavior, because the import of packages should not depend on whether or not the respective Jar has folder entries or not.

Extend syntax for simple class name handling

While one can do pretty much any filtering / assertion on class names using haveNameMatching(regex), common use cases are to check the simple name for endsWith, startsWith or contains.
These cases should be directly represented within the syntax, without thinking in regex. Thus we should add classes().

  • that().haveSimpleNameEndingWith(..)
  • that().haveSimpleNameStartingWith(..)
  • that().haveSimpleNameContaining(..)
  • should().haveSimpleNameEndingWith(..)
  • should().haveSimpleNameStartingWith(..)
  • should().haveSimpleNameContaining(..)

and the respective negations (notHave...)

@ArchIgnore is ignored on ArchRules fields

ArchRule suites ignore the @ArchIgnore annotation, e.g.:

public class MyRules {
    @ArchTest
    public static final ArchRule someRule = ...
}
@RunWith(ArchUnitRunner.class)
// ...
public class MyArchTest {
    @ArchTest
    @ArchIgnore // This is ignored
    public static final ArchRules rules = ArchRules.in(MyRules.class);
}

Expected behavior when running MyArchTest:
All rules within MyRules are skipped

Actual behavior:
Rules within MyRules run anyway.

NPE when layer name does not exists

I made a mistake in my layeredArchitecture definition. There is no layer called control:

   @ArchTest
    public static final ArchRule layerRule = layeredArchitecture()
            .layer("boundary").definedBy("..boundary..")
            .layer("service").definedBy("..control.service..")
            .layer("repository").definedBy("..control.repository..")
            .layer("entity").definedBy("..entity..")

            .whereLayer("boundary").mayNotBeAccessedByAnyLayer()
            .whereLayer("repository").mayOnlyBeAccessedByLayers("boundary", "control")
            .whereLayer("service").mayOnlyBeAccessedByLayers("boundary")
            .whereLayer("entity").mayOnlyBeAccessedByLayers("boundary", "control");

When I execute the test I get a NPE:

java.lang.NullPointerException
	at com.tngtech.archunit.library.Architectures$LayeredArchitecture$LayerDefinition.access$500(Architectures.java:229)
	at com.tngtech.archunit.library.Architectures$LayeredArchitecture.packagesOf(Architectures.java:218)
	at com.tngtech.archunit.library.Architectures$LayeredArchitecture.evaluate(Architectures.java:151)
	at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:79)
	at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:174)
	at com.tngtech.archunit.junit.ArchRuleExecution.evaluateOn(ArchRuleExecution.java:57)
	at com.tngtech.archunit.junit.ArchUnitRunner.runChild(ArchUnitRunner.java:134)
	at com.tngtech.archunit.junit.ArchUnitRunner.runChild(ArchUnitRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at com.tngtech.archunit.junit.ArchUnitRunner$1.evaluate(ArchUnitRunner.java:73)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

It would be great to print a meaningful error message instead of the NPE.

Fails with InvalidPathException when launched from Eclipse

I just tried to do a very simple case in my eclipse project and I'm getting an InvalidPathException

        JavaClasses importedClasses = new ClassFileImporter().importPackages("com.mypackage");
java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Software/eclipse/eclipse-neon-developer/eclipse/configuration/org.eclipse.osgi/432/0/.cp/
	at sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182)
	at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153)
	at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
	at sun.nio.fs.WindowsPath.parse(WindowsPath.java:94)
	at sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:255)
	at java.nio.file.Paths.get(Paths.java:84)
	at com.tngtech.archunit.core.importer.UrlSource$From.newFileUri(UrlSource.java:92)
	at com.tngtech.archunit.core.importer.UrlSource$From.parseClassPathEntry(UrlSource.java:86)
	at com.tngtech.archunit.core.importer.UrlSource$From.findUrlsForClassPathProperty(UrlSource.java:79)
	at com.tngtech.archunit.core.importer.UrlSource$From.classPathSystemProperties(UrlSource.java:71)
	at com.tngtech.archunit.core.importer.LocationResolver$Legacy.resolveClassPath(LocationResolver.java:38)
	at com.tngtech.archunit.core.importer.Locations.getLocationsOf(Locations.java:88)
	at com.tngtech.archunit.core.importer.Locations.ofPackage(Locations.java:63)
	at com.tngtech.archunit.core.importer.ClassFileImporter.importPackages(ClassFileImporter.java:128)
	at com.tngtech.archunit.core.importer.ClassFileImporter.importPackages(ClassFileImporter.java:138)
	at com.fisglobal.apexcollateral.DependencyCycleIntegrationTest.findCycles(DependencyCycleIntegrationTest.java:14)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

It looks like this is a classpath entry that was added by eclipse when running the Junit test. I see that UrlSource is already catching MalformedURLException, should it also catch InvalidPathException?

ClassesShould.callMethod and subclasses

I want to check that no class prints exception stack traces directly to stderr. My attempt was this:

class ExampleTest {

    @Test
    void noMethodShouldCall_printStackTrace() {
        JavaClasses classes = new ClassFileImporter().importClasses(Example.class);
        noClasses().should().callMethod(Throwable.class, "printStackTrace").check(classes);
    }

    static class Example {

        void printStackTraceOfException() {
            try {
                throw new Exception("Hello World");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        void printStackTraceOfRuntimeException() {
            try {
                throw new RuntimeException("Hello World");
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }

        void logErrorOnException() {
            try {
                throw new Exception("Hello World");
            } catch (Exception e) {
                // log.error(...)
            }
        }
    }
}

Actual: The check is green.
Expectation: The check fails for printStackTraceOfException and printStackTraceOfRuntimeException.

If I replace to callMethod(Exception.class, "printStackTrace") then the check fails for printStackTraceOfException but not for printStackTraceOfRuntimeException.

ArchUnit needs better (user) documentation

when compared to jQAssist, ArchUnit has some way to go wrt user documentation and accessibility.

Simple proposal: setup a static site generator (e.g. jekyll): You might copy the configuration/setup from arc42.org and simply replace the content...
(there's already a dockerized version so you don't need to install jekyll, ruby etc.).

In case of questions with this setup, I'm happy to support...

Enable to import multiple Jars at once

Right now it's possible to import multiple packages and multiple Locations via ClassFileImporter, but only a single JarFile.
It would be consistent and more convenient, to allow importing multiple JarFiles as well.

Provide basic rule definitions for fields, methods, constructors...

We should add to ArchRuleDefinition the factory methods

  • GivenFields fields()
  • GivenMethods methods()
  • GivenConstructors constructors()
  • GivenCodeUnits codeUnits()
  • GivenMembers members()

that provide an easy entrance to speak about the respective objects, i.e. provide a that(predicate) and should(condition) fluent API. At some later point, this API can be extended for generally useful methods like declaredIn(clazz) or similar.

Ignore Patterns on message is too greedy

I learned about the archunit_ignore_patterns before the new documentation and treated the Regex as a way to ignore classes within the package. Today I learned that it is applied on the whole violation message. This is too much in my case, because the signature of a violating method uses a class from an ignored package.

Could there be a way to improve the FailureReport class and the global ignore file to allow more specific ignoring rules?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.