diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 863629f71b83af02e314c75291c720cb3ab97525..3331d414d75623be26fc380c0c51d4de09f5ff2d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -246,6 +246,22 @@ build-windows:
 # TESTS #
 #########
 
+unittest:
+  stage: test
+  tags:
+    - linux
+  needs:
+    - job: set-version
+      artifacts: true
+  script:
+    - python3 -m venv venv
+    - . venv/bin/activate
+    - pip3 install -U pip
+    - pip3 install -r ../full-requirements.txt
+    - pip3 install -U -r ../full-requirements.txt
+    - cd src
+    - python3 -m unittest discover -t .
+
 test-pep8:
   stage: test
   tags:
@@ -253,6 +269,7 @@ test-pep8:
   needs:
     - job: set-version
       artifacts: true
+    - job: unittest
   script:
     - mkdir -p pep8
     - cd pep8
@@ -266,32 +283,6 @@ test-pep8:
     - pycodestyle ../src
   allow_failure: true
 
-# test-windows:
-#   stage: test
-#   tags:
-#     - wine
-#   needs:
-#     - job: set-version
-#       artifacts: true
-#     - job: build-windows
-#       artifacts: true
-#   script:
-#     - cd windows\pamhyr
-#     - pamhyr\pamhyr.exe hello
-
-# test-linux:
-#   stage: test
-#   tags:
-#     - linux
-#   needs:
-#     - job: set-version
-#       artifacts: true
-#     - job: build-linux
-#       artifacts: true
-#   script:
-#     - cd linux/pamhyr
-#     - ./Pamhyr2 hello
-
 ############
 # PACKAGES #
 ############
diff --git a/src/Model/Study.py b/src/Model/Study.py
index 1369e26906dc44a396d6c9a9fdc46a4b72e70729..c6b482e2306639b2bd15311b7203d18db4c26f85 100644
--- a/src/Model/Study.py
+++ b/src/Model/Study.py
@@ -290,3 +290,11 @@ class Study(SQLModel):
 
         self._save_submodel([self._river])
         self.commit()
+
+    def close(self):
+        """Close db connection
+
+        Returns:
+            Nothing.
+        """
+        self._close()
diff --git a/src/Model/__init__.py b/src/Model/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..89e3fbe8b5c9d03daa6dcdb50838fb054f67a946
--- /dev/null
+++ b/src/Model/__init__.py
@@ -0,0 +1,17 @@
+# __init__.py -- Pamhyr
+# Copyright (C) 2023  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
diff --git a/src/Model/test_Model.py b/src/Model/test_Model.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8577494806abd2354089eccf8536f9d3646dc70
--- /dev/null
+++ b/src/Model/test_Model.py
@@ -0,0 +1,108 @@
+# test_Model.py -- Pamhyr
+# Copyright (C) 2023  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import os
+import unittest
+import tempfile
+
+from Model.Saved import SavedStatus
+from Model.Study import Study
+from Model.River import River
+
+class StudyTestCase(unittest.TestCase):
+    def test_create_study(self):
+        study = Study.new("foo", "bar")
+        self.assertEqual(study.name, "foo")
+        self.assertEqual(study.description, "bar")
+
+    def test_open_study(self):
+        study = Study.open("../tests_cases/Enlargement/Enlargement.pamhyr")
+        self.assertNotEqual(study, None)
+        self.assertEqual(study.name, "Enlargement")
+
+    def test_save_open_study(self):
+        study = Study.new("foo", "bar")
+        dir = tempfile.mkdtemp()
+        f = os.path.join(dir, "foo.pamhyr")
+
+        # Save study
+        study.filename = f
+        study.save()
+        study.close()
+
+        # Reopen study
+        study = Study.open(f)
+
+        # Check
+        self.assertNotEqual(study, None)
+        self.assertEqual(study.name, "foo")
+        self.assertEqual(study.description, "bar")
+
+    def test_create_study_river(self):
+        study = Study.new("foo", "bar")
+        self.assertNotEqual(study.river, None)
+
+class RiverTestCase(unittest.TestCase):
+    def test_create_river(self):
+        status = SavedStatus()
+        river = River(status=status)
+
+        self.assertNotEqual(river, None)
+
+    def test_create_river_nodes(self):
+        status = SavedStatus()
+        river = River(status=status)
+
+        self.assertNotEqual(river, None)
+
+        # Add nodes
+        n0 = river.add_node()
+        n1 = river.add_node(x=1.0, y=0.0)
+        n2 = river.add_node(x=0.0, y=1.0)
+
+        # Checks
+        self.assertEqual(river.nodes_counts(), 3)
+
+        nodes = river.nodes()
+        self.assertEqual(nodes[0], n0)
+        self.assertEqual(nodes[1], n1)
+        self.assertEqual(nodes[2], n2)
+
+    def test_create_river_edges(self):
+        status = SavedStatus()
+        river = River(status=status)
+
+        self.assertNotEqual(river, None)
+
+        # Add nodes
+        n0 = river.add_node()
+        n1 = river.add_node(x=1.0, y=0.0)
+        n2 = river.add_node(x=0.0, y=1.0)
+
+        self.assertEqual(river.nodes_counts(), 3)
+
+        # Add edges
+        e0 = river.add_edge(n0, n1)
+        e1 = river.add_edge(n1, n2)
+
+        # Checks
+        self.assertEqual(river.edges_counts(), 2)
+
+        edges = river.edges()
+        self.assertEqual(edges[0], e0)
+        self.assertEqual(edges[1], e1)
diff --git a/src/tools.py b/src/tools.py
index d286345a2f3245258300e9fc0af04758102de2f2..218bc78d1f30c1db7d01f8a111174417e4b9ce30 100644
--- a/src/tools.py
+++ b/src/tools.py
@@ -272,6 +272,10 @@ class SQL(object):
         logger.debug("SQL - commit")
         self._db.commit()
 
+    def _close(self):
+        self.commit()
+        self._db.close()
+
     def _fetch_string(self, s):
         return s.replace("&#39;", "'")