diff --git a/README.md b/README.md
index 1874d2c952ff47ea5c3358b9b699263f7b5d7de8..dc63023c9182a9e50b1f3de0409931a80965418c 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Zu diesem Zweck werden wir Git und GitLab einsetzten. Sollten Sie mehr Informati
 
 In diesem Dokument finden Sie die folgenden Informationen:
 
-- [Fachliche Projektbeschreibung](#fachliche-projectbeschreibung)
+- [Fachliche Projektbeschreibung](#fachliche-projektbeschreibung)
 - [Technische Projektbeschreibung](#technische-projektbeschreibung)
 - [Hinweis zur Autorenschaft](#hinweis-zur-autorenschaft)
 
diff --git a/src/main/java/de/hda/fbi/db2/api/Lab01Data.java b/src/main/java/de/hda/fbi/db2/api/Lab01Data.java
index 4893b5f32f6daf9cbfd39f5bbfc3a1c9b092bffa..4cb716ca4090edd99f9c842148eb9a9fbcd2843a 100644
--- a/src/main/java/de/hda/fbi/db2/api/Lab01Data.java
+++ b/src/main/java/de/hda/fbi/db2/api/Lab01Data.java
@@ -25,9 +25,10 @@ public abstract class Lab01Data {
   public abstract List<?> getCategories();
 
   /**
-   * Save the csv data in appropriate objects.
+   * Save the CSV data in appropriate objects.
    *
-   * @param additionalCsvLines is the csv data
+   * @param csvLines CSV lines, each line is a String array consisting of the columns of the line.
+   *                 The first line consists of the headers of the CSV columns.
    */
-  public abstract void loadCsvFile(List<String[]> additionalCsvLines);
+  public abstract void loadCsvFile(List<String[]> csvLines);
 }
diff --git a/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java b/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java
index 990b54a68ce0292cd4051435c207eae3b4a004eb..a353624684b2114e19c5a560de072dc3610647f4 100644
--- a/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java
+++ b/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java
@@ -16,11 +16,11 @@ public abstract class Lab02EntityManager {
   protected Lab01Data lab01Data;
 
   /**
-   * Setter for Lab01Data; Don't overwrite, you will break the automation.
+   * Setter for Lab01Data. Do not touch this method.
    *
    * @param lab01Data lab01Data
    */
-  public void setLab01Data(Lab01Data lab01Data) {
+  public final void setLab01Data(Lab01Data lab01Data) {
     this.lab01Data = lab01Data;
   }
 
diff --git a/src/main/java/de/hda/fbi/db2/api/Lab03Game.java b/src/main/java/de/hda/fbi/db2/api/Lab03Game.java
index a69ae81e465428ba742868adecc41279e49bfa74..316741a53ddd0ddfbef8d34e798b5130c4b8fb9a 100644
--- a/src/main/java/de/hda/fbi/db2/api/Lab03Game.java
+++ b/src/main/java/de/hda/fbi/db2/api/Lab03Game.java
@@ -26,16 +26,16 @@ public abstract class Lab03Game {
    *
    * @param lab01Data lab01Data
    */
-  public void setLab01Data(Lab01Data lab01Data) {
+  public final void setLab01Data(Lab01Data lab01Data) {
     this.lab01Data = lab01Data;
   }
 
   /**
-   * Setter fro Lab02EntityManager. Do not touch this method.
+   * Setter for Lab02EntityManager. Do not touch this method.
    *
    * @param lab02EntityManager lab02EntityManager
    */
-  public void setLab02EntityManager(Lab02EntityManager lab02EntityManager) {
+  public final void setLab02EntityManager(Lab02EntityManager lab02EntityManager) {
     this.lab02EntityManager = lab02EntityManager;
   }
 
@@ -48,7 +48,7 @@ public abstract class Lab03Game {
    * </p>
    *
    * <p>This function is primarily used for testing. There exists a version with user interaction
-   * which shall be used from the menu
+   * which shall be used from the menu.
    * </p>
    *
    * @param playerName The name for the new Player.
@@ -67,7 +67,7 @@ public abstract class Lab03Game {
    * </p>
    *
    * <p>This function is primarily used for user interaction. There exists a version used for
-   * testing, @see getOrCreatePlayer</p>
+   * testing, {@link #getOrCreatePlayer(String)}.</p>
    *
    * @return Player object which was created or retrieved.
    * @see Lab03Game#getOrCreatePlayer(String)
@@ -116,7 +116,7 @@ public abstract class Lab03Game {
    *
    * @param player    The Player which shall play the game.
    * @param questions The Questions which shall be asked during the game.
-   * @return A Game Object which contains an unplayed game
+   * @return A Game object which contains an unplayed game
    *     for the given player with the given questions.
    */
   public abstract Object createGame(Object player, List<?> questions);
@@ -127,7 +127,7 @@ public abstract class Lab03Game {
    * <p>There is also an interactive version of this function which shall be called from the menu.
    * </p>
    *
-   * @param game The Game Object which shall be played.
+   * @param game The Game object which shall be played.
    * @see Lab03Game#interactivePlayGame(Object)
    */
   public abstract void playGame(Object game);
@@ -138,15 +138,15 @@ public abstract class Lab03Game {
    * <p>This is the function that should be called from the menu. Here you can implement the
    * necessary user interaction for the playing of the game.</p>
    *
-   * @param game The Game Object which shall be played.
+   * @param game The Game object which shall be played.
    * @see Lab03Game#playGame(Object)
    */
   public abstract void interactivePlayGame(Object game);
 
   /**
-   * Persists a played game, including the player which played it.
+   * Persists a played game, including the player who played it.
    *
-   * @param game The Game Object to be persisted.
+   * @param game The Game object to be persisted.
    */
   public abstract void persistGame(Object game);
 }
diff --git a/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java b/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java
index 7ec83528b7a2ff0e2265b111e6da714d4682d416..656899a0ac3c0998f32fa820f6d0064e653377f3 100644
--- a/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java
+++ b/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java
@@ -25,29 +25,29 @@ public abstract class Lab04MassData {
   protected Lab03Game lab03Game;
 
   /**
-   * Setter for Lab01Data.
+   * Setter for Lab01Data. Do not touch this method.
    *
    * @param lab01Data lab01Data
    */
-  public void setLab01Data(Lab01Data lab01Data) {
+  public final void setLab01Data(Lab01Data lab01Data) {
     this.lab01Data = lab01Data;
   }
 
   /**
-   * Setter fro Lab02EntityManager.
+   * Setter for Lab02EntityManager.
    *
    * @param lab02EntityManager lab02EntityManager
    */
-  public void setLab02EntityManager(Lab02EntityManager lab02EntityManager) {
+  public final void setLab02EntityManager(Lab02EntityManager lab02EntityManager) {
     this.lab02EntityManager = lab02EntityManager;
   }
 
   /**
-   * Setter fro lab03Game.
+   * Setter for lab03Game.
    *
    * @param lab03Game lab03Game
    */
-  public void setLab03Game(Lab03Game lab03Game) {
+  public final void setLab03Game(Lab03Game lab03Game) {
     this.lab03Game = lab03Game;
   }
 
diff --git a/src/main/java/de/hda/fbi/db2/api/package-info.java b/src/main/java/de/hda/fbi/db2/api/package-info.java
index cb415459e259aa9ba613d2334056e5ef9426010b..41e09594edf0ecdab1b5f3a90fcbc0d9dc0d3b36 100644
--- a/src/main/java/de/hda/fbi/db2/api/package-info.java
+++ b/src/main/java/de/hda/fbi/db2/api/package-info.java
@@ -1,6 +1,4 @@
 /**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
+ * Package containing abstract classes to be implemented by students.
  */
 package de.hda.fbi.db2.api;
diff --git a/src/main/java/de/hda/fbi/db2/controller/Controller.java b/src/main/java/de/hda/fbi/db2/controller/Controller.java
index a10654d1edb1eee911083b36169e59c7b67fd0c1..79909a38f15cab2ec89da7b919c2c3325d94b17e 100644
--- a/src/main/java/de/hda/fbi/db2/controller/Controller.java
+++ b/src/main/java/de/hda/fbi/db2/controller/Controller.java
@@ -6,6 +6,8 @@ import de.hda.fbi.db2.api.Lab03Game;
 import de.hda.fbi.db2.api.Lab04MassData;
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
@@ -51,30 +53,50 @@ public class Controller {
     findImplementations();
   }
 
-  @SuppressWarnings("deprecation")
+  private static <T> T createInstance(Class<?> c) throws Exception {
+    if (c.isInterface()) {
+      throw new IllegalArgumentException(
+          "Class " + c.getSimpleName() + " must not be an interface");
+    }
+    if (Modifier.isAbstract(c.getModifiers())) {
+      throw new IllegalArgumentException("Class " + c.getSimpleName() + " must not be abstract");
+    }
+
+    Constructor<?> constructor;
+    try {
+      constructor = c.getConstructor();
+    } catch (NoSuchMethodException e) {
+      throw new IllegalArgumentException(
+          "Class " + c.getSimpleName() + " has no default constructor", e);
+    }
+
+    // Trust that the caller has verified class already
+    @SuppressWarnings("unchecked")
+    T result = (T) constructor.newInstance();
+    return result;
+  }
+
   private void findImplementations() {
     try {
       Enumeration<URL> elements = Thread.currentThread().getContextClassLoader()
-          .getResources(IMPL_PACKAGE_NAME.replace(".", "/"));
+          .getResources(IMPL_PACKAGE_NAME.replace('.', '/'));
       if (!elements.hasMoreElements()) {
         return;
       }
-      String test = elements.nextElement().getFile();
-      List<Class> classes = new ArrayList<>(findClasses(new File(test), IMPL_PACKAGE_NAME));
-      for (Class clazz : classes) {
-        Class interfaces = clazz.getSuperclass();
-        if (interfaces != null) {
-          if (interfaces.getSimpleName().equals(Lab01Data.class.getSimpleName())) {
-            lab01Data = (Lab01Data) clazz.newInstance();
-          } else if (interfaces.getSimpleName()
-              .equals(Lab02EntityManager.class.getSimpleName())) {
-            lab02EntityManager = (Lab02EntityManager) clazz.newInstance();
-          } else if (interfaces.getSimpleName()
-              .equals(Lab03Game.class.getSimpleName())) {
-            lab03Game = (Lab03Game) clazz.newInstance();
-          } else if (interfaces.getSimpleName()
-              .equals(Lab04MassData.class.getSimpleName())) {
-            lab04MassData = (Lab04MassData) clazz.newInstance();
+      // Convert URL -> URI -> File to properly handle special characters or spaces in file path
+      File directory = new File(elements.nextElement().toURI());
+      List<Class<?>> classes = new ArrayList<>(findClasses(directory, IMPL_PACKAGE_NAME));
+      for (Class<?> clazz : classes) {
+        Class<?> superclass = clazz.getSuperclass();
+        if (superclass != null) {
+          if (superclass == Lab01Data.class) {
+            lab01Data = createInstance(clazz);
+          } else if (superclass == Lab02EntityManager.class) {
+            lab02EntityManager = createInstance(clazz);
+          } else if (superclass == Lab03Game.class) {
+            lab03Game = createInstance(clazz);
+          } else if (superclass == Lab04MassData.class) {
+            lab04MassData = createInstance(clazz);
           }
         }
       }
@@ -105,13 +127,13 @@ public class Controller {
       lab04MassData.setLab03Game(lab03Game);
       lab04MassData.init();
     } catch (Exception e) {
-      e.printStackTrace();
+      throw new IllegalStateException("API classes are not implemented correctly", e);
     }
   }
 
-  private List<Class> findClasses(File directory, String packageName)
+  private List<Class<?>> findClasses(File directory, String packageName)
       throws ClassNotFoundException {
-    List<Class> classes = new ArrayList<>();
+    List<Class<?>> classes = new ArrayList<>();
     if (!directory.exists()) {
       return classes;
     }
@@ -144,7 +166,7 @@ public class Controller {
         lab01Data.loadCsvFile(additionalCsvLines);
       }
     } catch (URISyntaxException | IOException e) {
-      e.printStackTrace();
+      throw new RuntimeException("Failed reading CSV data", e);
     }
     isCsvRead = true;
   }
diff --git a/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java b/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java
index e41ec7163b8e1836fac043fc7c1ff30fe6528675..2c89531b8f7a4a15bd2e93c65ef58041ab970e83 100644
--- a/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java
+++ b/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java
@@ -1,10 +1,13 @@
 package de.hda.fbi.db2.controller;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.nio.file.Files;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
@@ -25,7 +28,7 @@ public class CsvDataReader {
 
   /**
    * Lists all available csv files in the resource folder.
-   * <strong>WARNING:</strong> May won't work on your machine
+   * <strong>WARNING:</strong> May not work on your machine
    *
    * @return list of csv filenames
    * @throws IOException        ioException
@@ -45,8 +48,8 @@ public class CsvDataReader {
 
       fileList = Arrays.stream(filenames)
           .filter(File::isFile)
-          .filter(file -> file.getName().endsWith(".csv"))
           .map(File::getName)
+          .filter(name -> name.endsWith(".csv"))
           .collect(Collectors.toList());
     }
 
@@ -56,51 +59,50 @@ public class CsvDataReader {
   /**
    * Reads the given embedded file and returns its content in an accessible form.
    *
-   * @param otherFile filename of csv file in resource folder
+   * @param resourceName filename of CSV file in resource folder
    * @return content of the related file as a list of split strings including the CSV-header at
    *     first position.
-   * @throws URISyntaxException uRISyntaxException
+   * @throws URISyntaxException uriSyntaxException
    * @throws IOException        iOException
    */
-  public static List<String[]> read(String otherFile) throws IOException, URISyntaxException {
+  public static List<String[]> read(String resourceName) throws IOException, URISyntaxException {
     try {
-      if (!getAvailableFiles().contains(otherFile)) {
+      if (!getAvailableFiles().contains(resourceName)) {
         throw new IOException("File not found in Resources.");
       }
     } catch (IOException ioe) {
       throw ioe;
     } catch (Exception e) {
-      log.warning("CsvDataReader.getAvailableFiles() threw a exception."
+      log.warning("CsvDataReader.getAvailableFiles() threw an exception."
           + " Skipping file verification.");
     }
-    final URL resource = CsvDataReader.class.getResource("/" + otherFile);
-    if (resource == null) {
-      throw new IllegalStateException("Unable to find the csv file.");
-    }
-    return readFile(new File(resource.toURI()));
+    return readCsvResource("/" + resourceName);
   }
 
   /**
-   * Reads the embedded <code>Wissenstest_sample200.csv</code> file and returns its content in an
+   * Reads the embedded {@code Wissenstest_sample200.csv} file and returns its content in an
    * accessible form.
    *
    * @return content of the related file as a list of split strings including the CSV-header at
    *     first position.
-   * @throws URISyntaxException uRISyntaxException
+   * @throws URISyntaxException uriSyntaxException
    * @throws IOException        iOException
    */
   public static List<String[]> read() throws URISyntaxException, IOException {
-    final URL resource = CsvDataReader.class.getResource("/Wissenstest_sample200.csv");
-    if (resource == null) {
-      throw new IllegalStateException("Unable to find the csv file.");
-    }
-    return readFile(new File(resource.toURI()));
+    return readCsvResource("/Wissenstest_sample200.csv");
   }
 
-  private static List<String[]> readFile(File file) throws IOException {
-    final List<String> lines = Files.readAllLines(file.toPath());
-    return lines.stream()
-        .map((line) -> line.split(SPLIT_CHARACTER))
-        .collect(Collectors.toList());
+  private static List<String[]> readCsvResource(String resourcePath) throws IOException {
+    InputStream inputStream = CsvDataReader.class.getResourceAsStream(resourcePath);
+    if (inputStream == null) {
+      throw new IllegalArgumentException("Resource '" + resourcePath + "' does not exist");
+    }
+    try (inputStream; BufferedReader reader = new BufferedReader(
+        new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+
+      return reader.lines()
+          .map(line -> line.split(SPLIT_CHARACTER))
+          .collect(Collectors.toList());
+    }
   }
 }
diff --git a/src/main/java/de/hda/fbi/db2/controller/MenuController.java b/src/main/java/de/hda/fbi/db2/controller/MenuController.java
index 5d141b1cc6be919c97b5e6259b5da5bd55e991f2..64f00a080476154e48eac6d69f44a21a452ac2c0 100644
--- a/src/main/java/de/hda/fbi/db2/controller/MenuController.java
+++ b/src/main/java/de/hda/fbi/db2/controller/MenuController.java
@@ -11,7 +11,7 @@ import java.util.List;
  */
 public class MenuController {
 
-  private Controller controller;
+  private final Controller controller;
 
   public MenuController(Controller controller) {
     this.controller = controller;
diff --git a/src/main/java/de/hda/fbi/db2/controller/package-info.java b/src/main/java/de/hda/fbi/db2/controller/package-info.java
index e404848f963fdb5340a4ba87b3d55311a0c55297..53f47bd79306fec36b99c75f5bac4918cadf1dcf 100644
--- a/src/main/java/de/hda/fbi/db2/controller/package-info.java
+++ b/src/main/java/de/hda/fbi/db2/controller/package-info.java
@@ -1,6 +1,6 @@
 /**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
+ * Package containing internal implementation of this lab project.
+ *
+ * <p><b>MUST NOT BE CHANGED BY STUDENTS</b></p>
  */
 package de.hda.fbi.db2.controller;
diff --git a/src/main/java/de/hda/fbi/db2/package-info.java b/src/main/java/de/hda/fbi/db2/package-info.java
deleted file mode 100644
index e1f0f0808efc6f88447dc024e09e67cca10f8199..0000000000000000000000000000000000000000
--- a/src/main/java/de/hda/fbi/db2/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package de.hda.fbi.db2;
diff --git a/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java b/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java
index cb077ff5dc7d8c0c06075d47cc2deda38d5f78ad..95017d7df8100a976fbcc11d09b100ed2357368b 100644
--- a/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java
+++ b/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java
@@ -1,6 +1,4 @@
 /**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
+ * Package for student defined entity classes.
  */
 package de.hda.fbi.db2.stud.entity;
diff --git a/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java b/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java
index a1c9d39cf003e8b59f4fadcadb166574a35f9454..5e5347885960e8ace7193471c1fccd4d8efff7d0 100644
--- a/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java
+++ b/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java
@@ -1,6 +1,4 @@
 /**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
+ * Package for student defined implementation logic.
  */
 package de.hda.fbi.db2.stud.impl;
diff --git a/src/test/java/de/hda/fbi/db2/test/Lab01Test.java b/src/test/java/de/hda/fbi/db2/test/Lab01Test.java
index 0a9b63213d9b5de45891fa435bdd126de32c6471..738251eb66bc7d7a96072b8b292fc11e5b2abd46 100644
--- a/src/test/java/de/hda/fbi/db2/test/Lab01Test.java
+++ b/src/test/java/de/hda/fbi/db2/test/Lab01Test.java
@@ -1,9 +1,13 @@
 package de.hda.fbi.db2.test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
+import de.hda.fbi.db2.api.Lab01Data;
 import de.hda.fbi.db2.controller.Controller;
-import org.junit.Assert;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -23,10 +27,17 @@ public class Lab01Test {
   @BeforeClass
   public static void init() {
     controller = Controller.getInstance();
-    if (controller.getLab01Data() == null) {
-      Assert.fail("Could not find Lab01Data Implementation");
-      System.exit(-1);
+    Lab01Data impl = controller.getLab01Data();
+    if (impl == null) {
+      fail("Could not find Lab01Data implementation");
     }
+
+    String expectedPackage = "de.hda.fbi.db2.stud.impl";
+    // Check startsWith to also allow subpackages
+    if (!impl.getClass().getName().startsWith(expectedPackage + '.')) {
+      fail("Implementation class should be in package " + expectedPackage);
+    }
+
     if (!controller.isCsvRead()) {
       controller.readCsv();
     }
@@ -34,27 +45,41 @@ public class Lab01Test {
 
   @Test
   public void test1CategorySize() {
-    assertEquals("There should be 51 categories", 51,
-        controller.getLab01Data().getCategories().size());
+    List<?> categories = controller.getLab01Data().getCategories();
+    assertNotNull("Categories must not be null", categories);
+    assertEquals("There should be 51 categories", 51, categories.size());
   }
 
   @Test
   public void test2QuestionSize() {
-    assertEquals("There should be 200 questions", 200,
-        controller.getLab01Data().getQuestions().size());
+    List<?> questions = controller.getLab01Data().getQuestions();
+    assertNotNull("Questions must not be null", questions);
+    assertEquals("There should be 200 questions", 200, questions.size());
   }
 
   @Test
   public void test3CategoryObject() {
-    Object testObject = controller.getLab01Data().getCategories().get(0);
-    assertEquals("Category object should be named 'Category'", "Category",
+    List<?> categories = controller.getLab01Data().getCategories();
+    assertNotNull("Categories must not be null", categories);
+    assertFalse("Categories must not be empty", categories.isEmpty());
+
+    Object testObject = categories.get(0);
+    assertEquals("Category class should be named 'Category'", "Category",
         testObject.getClass().getSimpleName());
+    assertEquals("Category class should be in correct package", "de.hda.fbi.db2.stud.entity",
+        testObject.getClass().getPackageName());
   }
 
   @Test
   public void test4QuestionObject() {
-    Object testObject = controller.getLab01Data().getQuestions().get(0);
-    assertEquals("Question object should be named 'Question'", "Question",
+    List<?> questions = controller.getLab01Data().getQuestions();
+    assertNotNull("Questions must not be null", questions);
+    assertFalse("Questions must not be empty", questions.isEmpty());
+
+    Object testObject = questions.get(0);
+    assertEquals("Question class should be named 'Question'", "Question",
         testObject.getClass().getSimpleName());
+    assertEquals("Question class should be in correct package", "de.hda.fbi.db2.stud.entity",
+        testObject.getClass().getPackageName());
   }
 }
diff --git a/src/test/java/de/hda/fbi/db2/test/Lab02Test.java b/src/test/java/de/hda/fbi/db2/test/Lab02Test.java
index 0160eca76b9923fce2c9c095eac99efc337c6de3..791e9983c92ec7b996099b6520786364ad4e370a 100644
--- a/src/test/java/de/hda/fbi/db2/test/Lab02Test.java
+++ b/src/test/java/de/hda/fbi/db2/test/Lab02Test.java
@@ -1,20 +1,27 @@
 package de.hda.fbi.db2.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
+
+import de.hda.fbi.db2.api.Lab02EntityManager;
 import de.hda.fbi.db2.controller.Controller;
-import java.lang.reflect.Method;
+import java.util.List;
+import javax.persistence.metamodel.Attribute;
 import javax.persistence.metamodel.EntityType;
 import javax.persistence.metamodel.Metamodel;
 import javax.persistence.metamodel.PluralAttribute;
 import javax.persistence.metamodel.SingularAttribute;
 import javax.persistence.metamodel.Type;
 import org.eclipse.persistence.internal.jpa.metamodel.AttributeImpl;
-import org.eclipse.persistence.internal.jpa.metamodel.EntityTypeImpl;
 import org.eclipse.persistence.internal.jpa.metamodel.SingularAttributeImpl;
 import org.eclipse.persistence.mappings.AggregateCollectionMapping;
 import org.eclipse.persistence.mappings.DatabaseMapping;
 import org.eclipse.persistence.mappings.DirectCollectionMapping;
 import org.eclipse.persistence.mappings.DirectMapMapping;
-import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -28,11 +35,11 @@ public class Lab02Test {
 
   private static Metamodel metaData;
 
-  private static EntityTypeImpl categoryEntity;
+  private static EntityType<?> categoryEntity;
 
-  private static EntityTypeImpl questionEntity;
+  private static EntityType<?> questionEntity;
 
-  private static EntityTypeImpl answerEntity;
+  private static EntityType<?> answerEntity;
 
   private static Controller controller;
 
@@ -40,15 +47,15 @@ public class Lab02Test {
     Lab02Test.metaData = metaData;
   }
 
-  private static synchronized void setCategoryEntity(EntityTypeImpl categoryEntity) {
+  private static synchronized void setCategoryEntity(EntityType<?> categoryEntity) {
     Lab02Test.categoryEntity = categoryEntity;
   }
 
-  private static synchronized void setQuestionEntity(EntityTypeImpl questionEntity) {
+  private static synchronized void setQuestionEntity(EntityType<?> questionEntity) {
     Lab02Test.questionEntity = questionEntity;
   }
 
-  private static synchronized void setAnswerEntity(EntityTypeImpl answerEntity) {
+  private static synchronized void setAnswerEntity(EntityType<?> answerEntity) {
     Lab02Test.answerEntity = answerEntity;
   }
 
@@ -58,13 +65,18 @@ public class Lab02Test {
   @BeforeClass
   public static void init() {
     controller = Controller.getInstance();
-    if (controller.getLab01Data() == null) {
-      Assert.fail("Could not find Lab01Data Implementation");
-      System.exit(-1);
+    // Skip test class if students have not implemented lab01 yet
+    assumeNotNull(controller.getLab01Data());
+
+    Lab02EntityManager impl = controller.getLab02EntityManager();
+    if (impl == null) {
+      fail("Could not find Lab02EntityManager implementation");
     }
-    if (controller.getLab02EntityManager() == null) {
-      Assert.fail("Could not find Lab02EntityManager Implementation");
-      System.exit(-1);
+
+    String expectedPackage = "de.hda.fbi.db2.stud.impl";
+    // Check startsWith to also allow subpackages
+    if (!impl.getClass().getName().startsWith(expectedPackage + '.')) {
+      fail("Implementation class should be in package " + expectedPackage);
     }
 
     if (!controller.isCsvRead()) {
@@ -80,56 +92,72 @@ public class Lab02Test {
   public void test1EntityManager() {
     try {
       if (controller.getLab02EntityManager().getEntityManager() == null) {
-        Assert.fail("Lab02EntityManager.getEntityManager() returns null");
+        fail("Lab02EntityManager.getEntityManager() returns null");
       }
       setMetaData(controller.getLab02EntityManager().getEntityManager().getMetamodel());
     } catch (Exception e) {
-      Assert.fail("Exception during entityManager creation");
+      fail("Exception during entityManager creation");
     }
   }
 
   @Test
   public void test2FindCategory() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
-    for (EntityType current : metaData.getEntities()) {
-      if (current.getName().toLowerCase().equals("category")) {
-        setCategoryEntity((EntityTypeImpl) current);
+    for (EntityType<?> current : metaData.getEntities()) {
+      if (current.getName().equalsIgnoreCase("category")) {
+        setCategoryEntity(current);
+
+        List<?> categories = controller.getLab01Data().getCategories();
+        if (categories != null && !categories.isEmpty()) {
+          Class<?> javaType = current.getJavaType();
+          assertSame("Category class used by Lab01Data should be same as entity type",
+              categories.get(0).getClass(), javaType);
+        }
+
         return;
       }
     }
-    Assert.fail("Could not find category entity");
+    fail("Could not find Category entity");
   }
 
   @Test
   public void test3FindQuestion() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
-    for (EntityType current : metaData.getEntities()) {
-      if (current.getName().toLowerCase().equals("question")) {
-        setQuestionEntity((EntityTypeImpl) current);
+    for (EntityType<?> current : metaData.getEntities()) {
+      if (current.getName().equalsIgnoreCase("question")) {
+        setQuestionEntity(current);
+
+        List<?> questions = controller.getLab01Data().getQuestions();
+        if (questions != null && !questions.isEmpty()) {
+          Class<?> javaType = current.getJavaType();
+          assertSame("Question class used by Lab01Data should be same as entity type",
+              questions.get(0).getClass(), javaType);
+        }
+
         return;
       }
     }
-    Assert.fail("Could not find question entity");
+    fail("Could not find Question entity");
   }
 
   @Test
   public void test4FindAnswer() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (questionEntity == null) {
-      Assert.fail("Could not find questionEntity");
+      fail("Could not find questionEntity");
     }
 
-    for (Object member : questionEntity.getAttributes()) {
-      DatabaseMapping mapping = ((AttributeImpl) member).getMapping();
+    for (Attribute<?, ?> member : questionEntity.getAttributes()) {
+      DatabaseMapping mapping = ((AttributeImpl<?, ?>) member).getMapping();
       if (mapping instanceof DirectMapMapping) {
         return;
       }
@@ -140,46 +168,46 @@ public class Lab02Test {
         return;
       }
     }
-    Assert.fail("Could not find a possible answer constellation in question");
+    fail("Could not find a possible answer constellation in Question");
   }
 
   @Test
   public void test5AnswerStringsInQuestion() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (questionEntity == null) {
-      Assert.fail("Could not find questionEntity");
+      fail("Could not find questionEntity");
     }
 
     int stringCount = 0;
     for (Object member : questionEntity.getAttributes()) {
       if (member instanceof SingularAttribute) {
-        if (((SingularAttribute) member).getType().getJavaType() == String.class) {
+        if (((SingularAttribute<?, ?>) member).getType().getJavaType() == String.class) {
           stringCount = stringCount + 1;
         }
       }
     }
 
     if (stringCount > 2) {
-      Assert.fail("Found more then 2 Strings in Question!");
+      fail("Found more then 2 Strings in Question!");
     }
   }
 
   @Test
   public void test6QuestionInCategory() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (questionEntity == null || categoryEntity == null) {
-      Assert.fail("Could not find questionEntity or categoryEntity");
+      fail("Could not find questionEntity or categoryEntity");
     }
 
     for (Object member : categoryEntity.getAttributes()) {
       try {
-        Type type = ((PluralAttribute) member).getElementType();
+        Type<?> type = ((PluralAttribute<?, ?, ?>) member).getElementType();
         if (type == questionEntity) {
           return;
         }
@@ -188,115 +216,124 @@ public class Lab02Test {
       }
     }
 
-    Assert.fail("Could not find questions in category");
+    fail("Could not find questions in category");
+  }
+
+  private static boolean hasEqualsMethod(Class<?> c) {
+    try {
+      c.getDeclaredMethod("equals", Object.class);
+      return true;
+    } catch (NoSuchMethodException e) {
+      return false;
+    }
+  }
+
+  // Explicitly want to test `equals` implementation; ignore IntelliJ warnings about `equals` misuse
+  @SuppressWarnings({"SimplifiableAssertion", "EqualsWithItself", "ConstantConditions"})
+  private static void assertEqualsImplementation(Object a, Object b) {
+    String className = a.getClass().getSimpleName();
+    assertTrue(className + " equals method should return true for `this`", a.equals(a));
+    assertFalse(className + " equals method should return false for `null`",
+        a.equals(null));
+    assertFalse(className + " equals method should return false for different objects",
+        a.equals(b));
   }
 
   @Test
-  public void test7EqualsFunction() {
+  public void test7EqualsMethod() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (questionEntity == null || categoryEntity == null) {
-      Assert.fail("Could not find questionEntity or categoryEntity");
+      fail("Could not find questionEntity or categoryEntity");
     }
 
-    boolean foundEquals = false;
-    for (Method method : questionEntity.getJavaType().getMethods()) {
-      if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName()
-          .equals("Object")) {
-        foundEquals = true;
-        break;
-      }
+    if (!hasEqualsMethod(questionEntity.getJavaType())) {
+      fail("Could not find equals method in Question entity");
     }
 
-    if (!foundEquals) {
-      Assert.fail("Could not find equals method in question entity");
-    }
+    List<?> questions = controller.getLab01Data().getQuestions();
+    Object question1 = questions.get(0);
+    Object question2 = questions.get(1);
+    assertEqualsImplementation(question1, question2);
 
-    foundEquals = false;
-    for (Method method : categoryEntity.getJavaType().getMethods()) {
-      if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName()
-          .equals("Object")) {
-        foundEquals = true;
-        break;
-      }
+    if (!hasEqualsMethod(categoryEntity.getJavaType())) {
+      fail("Could not find equals method in Category entity");
     }
 
-    if (!foundEquals) {
-      Assert.fail("Could not find equals method in category entity");
-    }
+    List<?> categories = controller.getLab01Data().getCategories();
+    Object category1 = categories.get(0);
+    Object category2 = categories.get(1);
+    assertEqualsImplementation(category1, category2);
 
     if (answerEntity != null) {
-      foundEquals = false;
-      for (Method method : answerEntity.getJavaType().getMethods()) {
-        if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName()
-            .equals("Object")) {
-          foundEquals = true;
-          break;
-        }
+      if (!hasEqualsMethod(answerEntity.getJavaType())) {
+        fail("Could not find equals method in Answer entity");
       }
+    }
+  }
+
+  private static boolean hasHashCodeMethod(Class<?> c) {
+    try {
+      c.getDeclaredMethod("hashCode");
+      return true;
+    } catch (NoSuchMethodException e) {
+      return false;
+    }
+  }
 
-      if (!foundEquals) {
-        Assert.fail("Could not find equals method in answer entity");
+  private static void assertHashCodeImplementation(List<?> objects) {
+    assertFalse(objects.isEmpty());
+
+    Object obj = objects.get(0);
+    String className = obj.getClass().getSimpleName();
+    assertEquals(className + " hashCode method should return consistent results",
+        obj.hashCode(), obj.hashCode());
+
+    // Check if student implementation just calls `super.hashCode()`
+    // Because super.hashCode() could coincidentally be the same as properly implemented hashCode()
+    // reduce likelihood for false positive test failure by checking all objects
+    boolean differsFromIdentityHashCode = false;
+    for (Object o : objects) {
+      if (o.hashCode() != System.identityHashCode(o)) {
+        differsFromIdentityHashCode = true;
+        break;
       }
     }
 
-    Object question1 = controller.getLab01Data().getQuestions().get(0);
-    Object question2 = controller.getLab01Data().getQuestions().get(1);
-    if (question1.equals(question2)) {
-      Assert.fail("Two different questions should be equal");
+    if (!differsFromIdentityHashCode) {
+      fail(className + " hashCode method seems to call super.hashCode()");
     }
   }
 
   @Test
-  public void test8HashCodeFunction() {
+  public void test8HashCodeMethod() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (questionEntity == null || categoryEntity == null) {
-      Assert.fail("Could not find questionEntity or categoryEntity");
+      fail("Could not find questionEntity or categoryEntity");
     }
 
-    boolean foundEquals = false;
-    for (Method method : questionEntity.getJavaType().getMethods()) {
-      if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName()
-          .equals("Object")) {
-        foundEquals = true;
-        break;
-      }
+    if (!hasHashCodeMethod(questionEntity.getJavaType())) {
+      fail("Could not find hashCode method in Question entity");
     }
 
-    if (!foundEquals) {
-      Assert.fail("Could not find hashCode method in question entity");
-    }
+    List<?> questions = controller.getLab01Data().getQuestions();
+    assertHashCodeImplementation(questions);
 
-    foundEquals = false;
-    for (Method method : categoryEntity.getJavaType().getMethods()) {
-      if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName()
-          .equals("Object")) {
-        foundEquals = true;
-        break;
-      }
+    if (!hasHashCodeMethod(categoryEntity.getJavaType())) {
+      fail("Could not find hashCode method in Category entity");
     }
 
-    if (!foundEquals) {
-      Assert.fail("Could not find hashCode method in category entity");
-    }
+    List<?> categories = controller.getLab01Data().getCategories();
+    assertHashCodeImplementation(categories);
 
     if (answerEntity != null) {
-      foundEquals = false;
-      for (Method method : answerEntity.getJavaType().getMethods()) {
-        if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName()
-            .equals("Object")) {
-          foundEquals = true;
-          break;
-        }
-      }
-
-      if (!foundEquals) {
-        Assert.fail("Could not find hashCode method in answer entity");
+      if (!hasHashCodeMethod(answerEntity.getJavaType())) {
+        fail("Could not find hashCode method in Answer entity");
       }
     }
   }
@@ -304,21 +341,21 @@ public class Lab02Test {
   @Test
   public void test9CategoryNameUnique() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     if (categoryEntity == null) {
-      Assert.fail("Could not find categoryEntity");
+      fail("Could not find categoryEntity");
     }
 
-    for (Object member : categoryEntity.getAttributes()) {
-      System.out.println();
+    for (Attribute<?, ?> member : categoryEntity.getAttributes()) {
       try {
-        String name = ((SingularAttributeImpl) member).getName();
+        SingularAttributeImpl<?, ?> attribute = (SingularAttributeImpl<?, ?>) member;
+        String name = attribute.getName();
         if (name.equals("name")) {
-          boolean isUnique = ((SingularAttributeImpl) member).getMapping().getField().isUnique();
+          boolean isUnique = attribute.getMapping().getField().isUnique();
           if (!isUnique) {
-            Assert.fail("Name in Category is not unique");
+            fail("Attribute 'name' in Category is not unique");
           } else {
             return;
           }
@@ -327,6 +364,6 @@ public class Lab02Test {
         //This is expected
       }
     }
-    Assert.fail("Could not find name in category");
+    fail("Could not find attribute 'name' in Category");
   }
 }
diff --git a/src/test/java/de/hda/fbi/db2/test/Lab03Test.java b/src/test/java/de/hda/fbi/db2/test/Lab03Test.java
index 2456633e3ec3ee99ff31d6678419ed394ec465e9..7f77dc49c2869e5d15e9dcc475a7a50902a24605 100644
--- a/src/test/java/de/hda/fbi/db2/test/Lab03Test.java
+++ b/src/test/java/de/hda/fbi/db2/test/Lab03Test.java
@@ -1,5 +1,11 @@
 package de.hda.fbi.db2.test;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
+
 import de.hda.fbi.db2.api.Lab01Data;
 import de.hda.fbi.db2.api.Lab03Game;
 import de.hda.fbi.db2.controller.Controller;
@@ -7,7 +13,6 @@ import java.util.ArrayList;
 import java.util.List;
 import javax.persistence.metamodel.EntityType;
 import javax.persistence.metamodel.Metamodel;
-import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -35,17 +40,18 @@ public class Lab03Test {
   @BeforeClass
   public static void init() {
     controller = Controller.getInstance();
-    if (controller.getLab01Data() == null) {
-      Assert.fail("Could not find Lab01Data Implementation");
-      System.exit(-1);
-    }
-    if (controller.getLab02EntityManager() == null) {
-      Assert.fail("Could not find Lab02EntityManager Implementation");
-      System.exit(-1);
+    // Skip test class if students have not implemented lab01 or lab02 yet
+    assumeNotNull(controller.getLab01Data(), controller.getLab02EntityManager());
+
+    Lab03Game impl = controller.getLab03Game();
+    if (impl == null) {
+      fail("Could not find Lab02EntityManager implementation");
     }
-    if (controller.getLab03Game() == null) {
-      Assert.fail("Could not find Lab03Game Implementation");
-      System.exit(-1);
+
+    String expectedPackage = "de.hda.fbi.db2.stud.impl";
+    // Check startsWith to also allow subpackages
+    if (!impl.getClass().getName().startsWith(expectedPackage + '.')) {
+      fail("Implementation class should be in package " + expectedPackage);
     }
 
     if (!controller.isCsvRead()) {
@@ -58,18 +64,18 @@ public class Lab03Test {
 
     try {
       if (controller.getLab02EntityManager().getEntityManager() == null) {
-        Assert.fail("Lab02EntityManager.getEntityManager() returns null");
+        fail("Lab02EntityManager.getEntityManager() returns null");
       }
       metaData = controller.getLab02EntityManager().getEntityManager().getMetamodel();
     } catch (Exception e) {
-      Assert.fail("Exception during entityManager creation");
+      fail("Exception during entityManager creation");
     }
   }
 
   @Test
   public void test1Functionality() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     Lab03Game gameController = controller.getLab03Game();
@@ -77,9 +83,16 @@ public class Lab03Test {
     List<Object> categories = new ArrayList<>();
     categories.add(lab01Data.getCategories().get(0));
     categories.add(lab01Data.getCategories().get(1));
+
     List<?> questions = gameController.getQuestions(categories, 2);
+    assertNotNull(questions);
+    assertFalse("Questions for categories should not be empty", questions.isEmpty());
+    assertTrue("Should at most return 2 questions", questions.size() <= 2);
+
     Object player = gameController.getOrCreatePlayer("PlayerName");
+    assertNotNull(player);
     Object game = gameController.createGame(player, questions);
+    assertNotNull(game);
     gameController.playGame(game);
     gameController.persistGame(game);
   }
@@ -87,7 +100,7 @@ public class Lab03Test {
   @Test
   public void test2FindGameEntity() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     Lab03Game gameController = controller.getLab03Game();
@@ -95,41 +108,49 @@ public class Lab03Test {
     List<Object> categories = new ArrayList<>();
     categories.add(lab01Data.getCategories().get(0));
     categories.add(lab01Data.getCategories().get(1));
+
     List<?> questions = gameController.getQuestions(categories, 2);
+    assertNotNull(questions);
+    assertFalse("Questions for categories should not be empty", questions.isEmpty());
+    assertTrue("Should at most return 2 questions", questions.size() <= 2);
+
     Object player = gameController.getOrCreatePlayer("PlayerName");
+    assertNotNull(player);
     Object game = gameController.createGame(player, questions);
+    assertNotNull(game);
 
     boolean gameFound = false;
-    for (EntityType<?> clazzes : metaData.getEntities()) {
-      if (clazzes.getJavaType() == game.getClass()) {
+    for (EntityType<?> classes : metaData.getEntities()) {
+      if (classes.getJavaType() == game.getClass()) {
         gameFound = true;
-        setGameEntity(clazzes);
+        setGameEntity(classes);
       }
     }
 
     if (!gameFound) {
-      Assert.fail("Could not find game class as entity");
+      fail("Could not find Game class as entity");
     }
   }
 
   @Test
   public void test3FindPlayerEntity() {
     if (metaData == null) {
-      Assert.fail("No MetaModel");
+      fail("No MetaModel");
     }
 
     Lab03Game gameController = controller.getLab03Game();
     Object player = gameController.getOrCreatePlayer("PlayerName");
+    assertNotNull(player);
 
     boolean playerFound = false;
-    for (EntityType<?> clazzes : metaData.getEntities()) {
-      if (clazzes.getJavaType() == player.getClass()) {
+    for (EntityType<?> classes : metaData.getEntities()) {
+      if (classes.getJavaType() == player.getClass()) {
         playerFound = true;
       }
     }
 
     if (!playerFound) {
-      Assert.fail("Could not find player class as entity");
+      fail("Could not find Player class as entity");
     }
   }
 }
diff --git a/src/test/java/de/hda/fbi/db2/test/package-info.java b/src/test/java/de/hda/fbi/db2/test/package-info.java
deleted file mode 100644
index 64e0bad0e6281968eef415c15701bba933bf151d..0000000000000000000000000000000000000000
--- a/src/test/java/de/hda/fbi/db2/test/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package de.hda.fbi.db2.test;