diff --git a/scope4j/build.gradle b/scope4j/build.gradle
index 74c3ca8b598d9fb9801d589d931b351e00ef7b25..cf6e3a0f0aec486203016ec437c1894f108d2572 100644
--- a/scope4j/build.gradle
+++ b/scope4j/build.gradle
@@ -23,6 +23,10 @@ idea {
     }
 }
 
+test {
+    useJUnitPlatform {}
+}
+
 sourceSets.main {
     java {
 
@@ -37,6 +41,10 @@ sourceSets.main {
 }
 
 dependencies {
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
+    testCompile 'org.junit.platform:junit-platform-runner:1.4.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'
 }
 
 jastadd {
diff --git a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
index 9ac98896cb78232b3c06eaacd43ca5058caf8dbd..333ad33a87634ace49e97261fe6d3db3c156e362 100644
--- a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
+++ b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
@@ -18,6 +18,12 @@ public class ScopeAnalysis extends Frontend {
   }
 
 
+  public Program getProgram() {
+    return program;
+  }
+
+  private Program program;
+
   /**
    * Entry point for the Java checker.
    *
@@ -28,13 +34,24 @@ public class ScopeAnalysis extends Frontend {
     List<String> arguments = new ArrayList<>(Arrays.asList(args));
 
     boolean debug = arguments.isEmpty() || arguments.remove("--debug");
+    boolean tree = arguments.remove("--tree");
+    boolean warnings = arguments.remove("--warnings");
 
     if (arguments.size() > 1) {
-      System.out.println("usage: ScopeAnalysis [--debug] <directory with java files>");
+      System.out.println("usage: ScopeAnalysis [--debug] [--tree] [--warnings] <directory with java files>");
       System.exit(-1);
     }
     String path = arguments.isEmpty() ? "../testprograms/simpleScope" : arguments.get(arguments.size() - 1);
 
+    if (debug) {
+      new ScopeAnalysis().analyze(path, tree, warnings);
+    } else {
+      new ScopeAnalysis().analyzeTimed(path);
+    }
+
+  }
+
+  public void analyzeTimed(String path) {
     try {
       List<String> files = Files.walk(Paths.get(path))
           .filter(Files::isRegularFile)
@@ -43,37 +60,17 @@ public class ScopeAnalysis extends Frontend {
       // measure the time (with parsing) from here
       long startMeasurementTime = System.nanoTime();
 
-      Program program = new ScopeAnalysis().readProgram(files);
+      program = readProgram(files);
 
       // measure the time (without parsing) from here
       long startGenerationTime = System.nanoTime();
 
       ScopeTree scopeTree = program.scopeTree();
 
-      if (debug) {
-        scopeTree.printAST();
-
-        System.out.println("\nExtendJ found the following problems:");
-        for (CompilationUnit unit : program.getCompilationUnitList()) {
-          for (Problem problem : unit.problems()) {
-            System.out.println(problem);
-          }
-        }
-        System.out.println();
-      }
-
       long startAnalysisTime = System.nanoTime();
 
       Set<VariableShadowFinding> findings = scopeTree.variableShadowings();
 
-      if (debug) {
-        System.out.println("\nScope4J found the following problems:");
-        for (VariableShadowFinding finding : findings) {
-          System.out.println(finding);
-        }
-        System.out.println();
-      }
-
       // measure the time until here
       long endTime = System.nanoTime();
 
@@ -100,6 +97,45 @@ public class ScopeAnalysis extends Frontend {
     }
   }
 
+
+  public Set<VariableShadowFinding> analyze(String path, boolean tree, boolean warnings) {
+    try {
+      List<String> files = Files.walk(Paths.get(path))
+          .filter(Files::isRegularFile)
+          .filter(x -> x.getFileName().toString().endsWith(".java")).map(Path::toString).collect(Collectors.toList());
+
+      program = readProgram(files);
+
+      ScopeTree scopeTree = program.scopeTree();
+
+      if (tree) {
+        scopeTree.printAST();
+      }
+
+      if (warnings) {
+        System.out.println("\nExtendJ found the following problems:");
+        for (CompilationUnit unit : program.getCompilationUnitList()) {
+          for (Problem problem : unit.problems()) {
+            System.out.println(problem);
+          }
+        }
+        System.out.println();
+      }
+
+      Set<VariableShadowFinding> findings = scopeTree.variableShadowings();
+
+      System.out.println("\nScope4J found the following problems:");
+      for (VariableShadowFinding finding : findings) {
+        System.out.println(finding);
+      }
+      System.out.println();
+
+      return findings;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   private Program readProgram(Collection<String> files) throws IOException {
 
 
diff --git a/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..10bdd1e5861f6e00273dc24e91a42a6724fbb4e6
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
@@ -0,0 +1,21 @@
+package org.extendj;
+
+import org.extendj.ast.Declaration;
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public abstract class ScopeAnalysisTest {
+  static void assertShadow(Set<VariableShadowFinding> findings, String name, int shadowerLine, int shadowedLine) {
+    for (VariableShadowFinding finding : findings) {
+      Declaration shadower = finding.getShadower();
+      Declaration shadowed = finding.getShadowed();
+      if (shadowed.getName().equals(name) && shadowed.lineNumber() == shadowedLine && shadower.lineNumber() == shadowerLine) {
+        return;
+      }
+    }
+    Assertions.fail("No finding found for name '" + name + "' in lines " + shadowerLine + " > " + shadowedLine);
+  }
+}
diff --git a/scope4j/src/test/java/org/extendj/SimpleScopeTest.java b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c11b50280b627dc00575411e672cffd412de697f
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
@@ -0,0 +1,29 @@
+package org.extendj;
+
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public class SimpleScopeTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<VariableShadowFinding> findings = scopeAnalysis.analyze("src/test/resources/simple", false, false);
+
+
+    Assertions.assertEquals(5, findings.size());
+
+    assertShadow(findings, "localVarA", 19, 11);
+    assertShadow(findings, "localVarB", 33, 12);
+    assertShadow(findings, "localVarC", 24, 13);
+    assertShadow(findings, "fieldA", 29, 3);
+    assertShadow(findings, "fieldB", 36, 4);
+
+
+  }
+
+}
diff --git a/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dd2b9e8b86dd95db63201b7c707978cfa7a30fb
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
@@ -0,0 +1,26 @@
+package org.extendj;
+
+import org.extendj.ast.VariableShadowFinding;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public class SuperclassFieldsTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<VariableShadowFinding> findings = scopeAnalysis.analyze("src/test/resources/superclassFields", false, false);
+
+
+    assertShadow(findings, "fieldC", 19, 3);
+    assertShadow(findings, "fieldB", 21, 2);
+    assertShadow(findings, "fieldB", 2, 4);
+
+    Assertions.assertEquals(3, findings.size());
+
+  }
+
+}
diff --git a/scope4j/src/test/resources/simple/ClassA.java b/scope4j/src/test/resources/simple/ClassA.java
new file mode 100644
index 0000000000000000000000000000000000000000..a066a2aa6bdf14ea306e3c0c077723250ced13fb
--- /dev/null
+++ b/scope4j/src/test/resources/simple/ClassA.java
@@ -0,0 +1,42 @@
+public abstract class ClassA {
+
+  int fieldA;
+  int fieldB;
+
+  public ClassA(int constructorParameterA) {
+    int localConstructorVarA = 0;
+  }
+
+  public void methodNameA(int parameterA) {
+    int localVarA = 1;
+    int localVarB = 1;
+    int localVarC = 1;
+
+    {
+      int localVarInBlockA = 2;
+
+      // this is shadowing (and forbidden)
+      int localVarA = 3;
+    }
+
+    class Local {
+      {
+        for (int localVarC = 0; localVarC < 10; localVarC++) System.out.println(localVarC);
+      }
+    }
+
+    // this is shadowing (over two levels, not forbidden)
+    int fieldA;
+
+    try (
+        // this is forbidden
+        java.util.zip.ZipFile localVarB = new java.util.zip.ZipFile("zipFileName");
+
+        // this is okay
+        java.io.BufferedWriter fieldB = java.nio.file.Files.newBufferedWriter(null)
+    ) { /* do stuff */ } catch (java.io.IOException e) {/* do stuff */}
+  }
+
+  // this does not appear as a scope (and, more importantly, the parameters are not added anywhere else)
+  public abstract void methodNameB(int parameterForAbstractMethodB);
+}
diff --git a/scope4j/src/test/resources/superclassFields/ClassA.java b/scope4j/src/test/resources/superclassFields/ClassA.java
new file mode 100644
index 0000000000000000000000000000000000000000..675b9360cbfcb0764893486d7dcc1946b2c10085
--- /dev/null
+++ b/scope4j/src/test/resources/superclassFields/ClassA.java
@@ -0,0 +1,12 @@
+public abstract class ClassA {
+​
+  int fieldA;
+  int fieldB;
+
+  void m();
+
+  void n() {
+    //...
+  }
+​
+}
diff --git a/scope4j/src/test/resources/superclassFields/ClassB.java b/scope4j/src/test/resources/superclassFields/ClassB.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbd482e62aeabbb93dc24c05192314c39ee5c9a1
--- /dev/null
+++ b/scope4j/src/test/resources/superclassFields/ClassB.java
@@ -0,0 +1,26 @@
+public class ClassB extends ClassA {
+  int fieldB;
+  int fieldC;
+
+  @Override
+  void m() {
+    //Overridden..
+  }
+
+  void n() {
+    //not overriden
+  }
+
+  void n(int value) {
+    //polymorphic
+  }
+
+  class ClassC {
+    int fieldC;
+
+    public ClassC(int fieldB) {
+      fieldC = fieldB;
+    }
+  }
+​
+}