Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: one test was not called correctly, hence was not run by Maven in CI #1036

Merged
merged 1 commit into from
Dec 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions doc/architecture_test.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ title: Using Spoon for Architecture Enforcement
tags: [getting-started]
---

Spoon can be used to check the architecture rules of your application.
For this, the idea is to write a test case that checks the rule.
Spoon can be used to check the architectural rules of your application.
For this, the idea is to write a standard Junit test case that loads the application code and checks the rule.
You only need to depend on spoon at testing time, ie `<scope>test</scope>` in Maven.

Example: rules on constructor usage
----------------------------------

For instance, let's imagine that you want to forbid the usage of `TreeSet`, in your code base, you would simply write a test case as follows:

```java
void noTreeSetInSpoon() throws Exception {
// we don't use TreeSet, because they implicitly depend on Comparable (no static check, only dynamic checks)
@Test
void noTreeSet() throws Exception {
SpoonAPI spoon = new Launcher();
spoon.addInputResource("src/main/java/");
spoon.buildModel();
Expand All @@ -27,5 +32,26 @@ That's it! Every time you run the tests, incl. on your continuous integration se

For instance, you can check that you never return null, or always use an appropriate factory, or that all classes implementing an interface are in the same package.

Note that you only need to depend on spoon at testing time, ie `<scope>test</scope>` in Maven.

Example: checking naming conventions for test cases
----------------------------

A common mistake is to forget to follow a naming convention. For instance, if you use Maven, all test classes must be named `Test*` or `*Test` in order to be run by Maven's standard test plugin `surefire` ([see doc](http://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html)). This rule simply reads:

```java
@Test
public void testGoodTestClassNames() throws Exception {
SpoonAPI spoon = new Launcher();
spoon.addInputResource("src/test/java/");
spoon.buildModel();

for (CtMethod<?> meth : spoon.getModel().getRootPackage().getElements(new TypeFilter<CtMethod>(CtMethod.class) {
@Override
public boolean matches(CtMethod element) {
return super.matches(element) && element.getAnnotation(Test.class) != null;
}
})) {
assertTrue("naming contract violated for "+meth.getParent(CtClass.class).getSimpleName(), meth.getParent(CtClass.class).getSimpleName().startsWith("Test") || meth.getParent(CtClass.class).getSimpleName().endsWith("Test"));
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtVisitor;
import spoon.support.comparator.SignatureComparator;
import spoon.support.util.SignatureBasedSortedSet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class CtEnumImpl<T extends Enum<?>> extends CtClassImpl<T> implements CtEnum<T> {
private static final long serialVersionUID = 1L;
Expand All @@ -47,7 +46,7 @@ public void accept(CtVisitor visitor) {

@Override
public Set<CtMethod<?>> getAllMethods() {
Set<CtMethod<?>> allMethods = new TreeSet<>(new SignatureComparator());
Set<CtMethod<?>> allMethods = new SignatureBasedSortedSet();
allMethods.addAll(getMethods());
allMethods.addAll(getFactory().Type().get(Enum.class).getMethods());
allMethods.add(valuesMethod());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import spoon.reflect.visitor.filter.ReferenceTypeFilter;
import spoon.support.UnsettableProperty;
import spoon.support.SpoonClassNotFoundException;
import spoon.support.comparator.SignatureComparator;
import spoon.support.compiler.SnippetCompilationHelper;
import spoon.support.util.QualifiedNameBasedSortedSet;
import spoon.support.util.SignatureBasedSortedSet;
Expand All @@ -59,7 +58,6 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import static spoon.reflect.ModelElementContainerDefaultCapacities.TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY;

Expand Down Expand Up @@ -884,7 +882,7 @@ public Collection<CtExecutableReference<?>> getDeclaredExecutables() {

@Override
public Collection<CtExecutableReference<?>> getAllExecutables() {
Set<CtExecutableReference<?>> l = new TreeSet(new SignatureComparator());
Set<CtExecutableReference<?>> l = new SignatureBasedSortedSet();
for (CtMethod<?> m : getAllMethods()) {
l.add((CtExecutableReference<?>) m.getReference());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
import spoon.Launcher;
import spoon.SpoonAPI;
import spoon.reflect.code.CtConstructorCall;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.visitor.filter.AbstractFilter;
import spoon.support.DefaultCoreFactory;
import spoon.support.compiler.SnippetCompilationHelper;
import spoon.reflect.visitor.filter.TypeFilter;

import java.util.TreeSet;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class SpoonArchitectureEnforcer {
public class SpoonArchitectureEnforcerTest {

@Test
public void statelessFactory() throws Exception {
Expand Down Expand Up @@ -83,4 +83,26 @@ public void metamodelPackageRule() throws Exception {
assertTrue(implType.getReference().isSubtypeOf(interfaceType.getReference()));
}
}


@Test
public void testGoodTestClassNames() throws Exception {
// contract: to be run by Maven surefire, all test classes must be called Test* or *Test
// reference: "By default, the Surefire Plugin will automatically include all test classes with the following wildcard patterns:"
// "**/Test*.java" and "**/*Test.java"
// http://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html
SpoonAPI spoon = new Launcher();
spoon.addInputResource("src/test/java/");
spoon.buildModel();

for (CtMethod<?> meth : spoon.getModel().getRootPackage().getElements(new TypeFilter<CtMethod>(CtMethod.class) {
@Override
public boolean matches(CtMethod element) {
return super.matches(element) && element.getAnnotation(Test.class) != null;
}
})) {
assertTrue("naming contract violated for "+meth.getParent(CtClass.class).getSimpleName(), meth.getParent(CtClass.class).getSimpleName().startsWith("Test") || meth.getParent(CtClass.class).getSimpleName().endsWith("Test"));
}
}

}