diff --git a/soitool/modules/module_authentification_board.py b/soitool/modules/module_authentification_board.py
index a25512c40e2b7d09bc5dd51ceee2cf793fb89701..11e6091a6792cb1507175274743d2afd80fbc4ba 100644
--- a/soitool/modules/module_authentification_board.py
+++ b/soitool/modules/module_authentification_board.py
@@ -14,7 +14,7 @@ from soitool.modules.module_base import (
 START_NO_OF_AUTHENTICATION_CODES = 10
 CODE_LENGTH = 26
 CODE_CHARACTERS = string.ascii_uppercase  # Codes consist of these characters
-ROW_IDENTIFIERS = string.ascii_uppercase  # Characters for second column,
+ROW_IDENTIFIERS = string.ascii_uppercase  # Characters for first column,
 # it's length determines maximum number of codes (rows).
 SEPARATE_INTERVAL = 5  # Adds space between sets of characters, 0 => no spaces
 # If code is 123456 and interval is 2, code will be 12 34 56
@@ -152,7 +152,9 @@ class AuthentificationBoardModule(ModuleBase, QTableWidget, metaclass=Meta):
             start = SEPARATE_INTERVAL
 
         # Separate code with spaces
-        for i in range(start, len(code), SEPARATE_INTERVAL):
+        for i in range(
+            start, len(code) - SEPARATE_INTERVAL, SEPARATE_INTERVAL
+        ):
             code[i] += " "
 
         return "".join(code)
@@ -245,8 +247,8 @@ class AuthentificationBoardModule(ModuleBase, QTableWidget, metaclass=Meta):
 
         Returns
         -------
-        List (2D)
-            list[0][0] contains headline,
+        List
+            List[0] contains headline,
             list[x][y] represents value of row x, column y.
         """
         content = []
@@ -303,10 +305,10 @@ def generate_authentification_codes(amount=START_NO_OF_AUTHENTICATION_CODES):
             start = SEPARATE_INTERVAL - 1
         else:
             start = SEPARATE_INTERVAL
-        for j in range(
+        for i in range(
             start, len(sequence) - SEPARATE_INTERVAL, SEPARATE_INTERVAL
         ):
-            sequence[j] += " "
+            sequence[i] += " "
         codes.append("".join(sequence))
 
     return codes
diff --git a/test/test_module_authentification_board.py b/test/test_module_authentification_board.py
new file mode 100644
index 0000000000000000000000000000000000000000..19a1773d79413499724c606db0d21128487f676c
--- /dev/null
+++ b/test/test_module_authentification_board.py
@@ -0,0 +1,173 @@
+"""Test AuthentificationBoardModule."""
+import unittest
+from PySide2 import QtGui
+from PySide2.QtWidgets import QApplication
+from PySide2.QtCore import Qt
+from PySide2.QtTest import QTest
+from soitool.modules.module_authentification_board import (
+    AuthentificationBoardModule,
+    START_NO_OF_AUTHENTICATION_CODES,
+    HEADLINE_TEXT,
+    ROW_IDENTIFIERS,
+    CODE_LENGTH,
+    CODE_CHARACTERS,
+)
+from soitool.soi import SOI
+
+if isinstance(QtGui.qApp, type(None)):
+    app = QApplication([])
+else:
+    app = QtGui.qApp
+
+
+class TestDefaultAuthentificationBoardModule(unittest.TestCase):
+    """TestCase for AuthentificationBoardModule."""
+
+    def setUp(self):
+        """Create new AuthentificationBoardModule."""
+        self.module = AuthentificationBoardModule()
+
+    def test_default_module(self):
+        """Test that module is initialized properly."""
+        # Assert correct headline
+        self.assertEqual(self.module.item(0, 0).text(), HEADLINE_TEXT)
+
+        # Assert correct number of rows including header
+        self.assertEqual(
+            self.module.rowCount(), START_NO_OF_AUTHENTICATION_CODES + 1
+        )
+        # Assert correct number of columns
+        self.assertEqual(self.module.columnCount(), 3)
+
+        # Assert cell content in first column is correct
+        self.assertEqual(self.module.item(1, 0).text(), ROW_IDENTIFIERS[0])
+        self.assertEqual(self.module.item(2, 0).text(), ROW_IDENTIFIERS[1])
+
+        # Assert cell content in second column is correct
+        self.assertEqual(self.module.item(1, 1).text(), "0")
+        self.assertEqual(self.module.item(2, 1).text(), "1")
+
+        # Assert cell content in third column is correct
+        code = self.module.item(1, 2).text()
+        self.assertTrue(len(code) >= CODE_LENGTH)
+        # Assert all code characters are taken from list CODE_CHARACTERS
+        for _, character in enumerate(code):
+            if character != " ":
+                self.assertTrue(character in CODE_CHARACTERS)
+        # Assert all codes are unique
+        code_list = self.module.get_codes()
+        code_set = set(code_list)
+        self.assertEqual(len(code_list), len(code_set))
+
+    def test_generate_unique_authentification_code(self):
+        """Test function generate_unique_authentification_module."""
+        code_list = self.module.get_codes()
+        generated_code = self.module.generate_unique_authentification_code()
+
+        # Assert code length is equal to an existing code
+        self.assertEqual(len(generated_code), len(code_list[0]))
+        # Assert generated code is not equal to any existing codes
+        self.assertFalse(generated_code in code_list)
+
+    def test_get_codes(self):
+        """Test function get_codes."""
+        # Get codes
+        code_list = self.module.get_codes()
+
+        # Assert codes are correct
+        for i, code in enumerate(code_list):
+            self.assertEqual(code, self.module.item(i + 1, 2).text())
+
+    def test_gui_add_row(self):
+        """Test adding rows with shortcuts."""
+        # Widget must be shown for shortcuts to work
+        self.module.show()
+        QTest.qWaitForWindowExposed(self.module)
+
+        old_row_count = self.module.rowCount()
+
+        # Use shortcut 'Ctrl + +'
+        QTest.keyClicks(self.module, "+", Qt.ControlModifier)
+
+        # Assert a new row is added
+        new_row_count = self.module.rowCount()
+        self.assertEqual(old_row_count + 1, new_row_count)
+
+        # Assert new row has correct content
+        row_index = new_row_count - 1
+        self.assertEqual(
+            self.module.item(row_index, 0).text(),
+            ROW_IDENTIFIERS[row_index - 1],
+        )
+        self.assertEqual(
+            self.module.item(row_index, 1).text(), str(row_index - 1)
+        )
+        new_code = self.module.item(row_index, 2).text()
+        existing_code = self.module.item(1, 2).text()
+        self.assertEqual(len(new_code), len(existing_code))
+
+    def test_gui_remove_row(self):
+        """Test removing rows with shortcuts."""
+        # Widget must be shown for shortcuts to work
+        self.module.show()
+        QTest.qWaitForWindowExposed(self.module)
+
+        old_row_count = self.module.rowCount()
+
+        # First row is selected on startup, it contains headline
+        # and user should not be able to delete it with shortcut
+        QTest.keyClicks(self.module, "_", Qt.ControlModifier)
+        # Assert row was not removed
+        new_row_count = self.module.rowCount()
+        self.assertEqual(old_row_count, new_row_count)
+
+        # Move to first row, then use shortcut to delete it
+        QTest.keyClick(self.module, Qt.Key_Down)
+        QTest.keyClicks(self.module, "_", Qt.ControlModifier)
+        # Assert row was removed and replaced by the row below
+        value_to_delete = self.module.item(1, 1).text()
+        new_row_count = self.module.rowCount()
+        self.assertEqual(old_row_count - 1, new_row_count)
+        self.assertEqual(self.module.item(1, 1).text(), value_to_delete)
+
+        # Remove rows until only headline-row and a single code-row exist
+        for _ in range(1, self.module.rowCount() - 1):
+            QTest.keyClick(self.module, Qt.Key_Down)
+            QTest.keyClicks(self.module, "_", Qt.ControlModifier)
+        self.assertTrue(self.module.rowCount() == 2)
+        # Try to remove final code-row, should not work
+        QTest.keyClick(self.module, Qt.Key_Down)
+        QTest.keyClicks(self.module, "_", Qt.ControlModifier)
+        # Assert row was not removed
+        self.assertTrue(self.module.rowCount() == 2)
+
+    def test_add_to_soi_smoke_test(self):
+        """Test that module can be added to SOI."""
+        soi = SOI()
+        module_name = "Test name"
+        soi.add_module(module_name, self.module)
+        self.assertTrue(soi.module_name_taken(module_name))
+
+
+class TestAuthentificationBoardModuleFromData(unittest.TestCase):
+    """TestCase for initializing AuthentificationBoardModule from data."""
+
+    def test_create_from_data(self):
+        """Test creating AuthentificationBoardModule from data."""
+        test_data = [
+            "Headline text",
+            ["A", "0", "TEST CODE ONE"],
+            ["B", "1", "TEST CODE TWO"],
+        ]
+        test_size = {"width": 100, "height": 100}
+
+        module = AuthentificationBoardModule(size=test_size, data=test_data)
+
+        # Assert module contains expected data
+        self.assertEqual(module.item(0, 0).text(), "Headline text")
+        self.assertEqual(module.item(1, 0).text(), "A")
+        self.assertEqual(module.item(1, 1).text(), "0")
+        self.assertEqual(module.item(1, 2).text(), "TEST CODE ONE")
+        self.assertEqual(module.item(2, 0).text(), "B")
+        self.assertEqual(module.item(2, 1).text(), "1")
+        self.assertEqual(module.item(2, 2).text(), "TEST CODE TWO")