diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b33fbec465231be48a8bbae87394602a594c2560..f1b2890be4ff877cf94b71fd2bdf6a9852f4690d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -60,6 +60,7 @@ test:bsd:
     dependencies:
         - build:bsd
     script:
+        - make test
         - echo Successfully tested.
 
 test:deb:
@@ -68,4 +69,5 @@ test:deb:
         - build:deb
     script:
         - make lintian
+        - make test
         - echo Successfully tested.
diff --git a/Makefile b/Makefile
index d6cb71088c09b1134a8950a801bbeeb157a94a9c..300bce83648af2f52d240ba266bb9fd204d75189 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ DEPFLAGS = -MT $@ -MMD -MP -MF $(patsubst ${OBJDIR}/%.o,${DEPDIR}/%.d,$@)
 CFLAGS_DEBUG = -g -Og
 CFLAGS_RELEASE = -O3 -msse2 -mavx
 
+# TODO: introduce configure script
 CFLAGS += ${CFLAGS_DEBUG}
 
 PODOMAIN ?= rmrf
@@ -54,6 +55,10 @@ POTDIR ?= po/tpl
 PODIR ?= po/lang
 MODIR ?= po/bin
 
+TESTDIR ?= test
+TESTBINDIR ?= bin/test
+TESTOBJDIR ?= obj/test
+
 rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
 
 SOURCES := $(call rwildcard,${SRCDIR},*.cpp *.c)
@@ -65,6 +70,10 @@ OBJECTS := $(filter-out $(patsubst ${SRCDIR}/%,${OBJDIR}/%,${APPDIR})/%,${SRCOBJ
 
 TARGETS := $(patsubst $(patsubst ${SRCDIR}/%,${OBJDIR}/%,${APPDIR})/%.o,${BINDIR}/%,${APPOBJS})
 
+TEST_SOURCES := $(call rwildcard,${TESTDIR},*.cpp *.c)
+TEST_SRCOBJS := $(patsubst ${TESTDIR}/%.c,${TESTOBJDIR}/%.o,$(patsubst ${TESTDIR}/%.cpp,${TESTOBJDIR}/%.o,${TEST_SOURCES}))
+TEST_TARGETS := $(patsubst ${TESTOBJDIR}/%.o,${TESTBINDIR}/%,${TEST_SRCOBJS})
+
 POTSRCS := ${SOURCES} $(call rwildcard,${SRCDIR},*.hpp *.h)
 POTOBJS := ${POTDIR}/${PODOMAIN}.pot
 POOBJS := $(foreach POLANG,${POLANGS},$(patsubst ${POTDIR}/%.pot,${PODIR}/${POLANG}/%.po,${POTOBJS}))
@@ -77,8 +86,12 @@ MAKEFLAGS += --no-builtin-rules
 
 .PRECIOUS: ${DEPDIR}/%.d ${OBJDIR}/%.o ${POTOBJS} ${POOBJS}
 
-.PHONY: all clean install lintian style translation
-all: ${TARGETS} translation
+.PHONY: all build clean install lintian style test translation
+all: build translation
+	echo "Test targets:"
+	echo ${TEST_TARGETS}
+
+build: ${TARGETS}
 
 ${BINDIR}/%: $(patsubst ${SRCDIR}/%,${OBJDIR}/%,${APPDIR})/%.o ${OBJECTS} Makefile ${APPDIR}/%.ldflags
 	${MKDIR} ${@D} && ${CXX} ${CXXFLAGS} ${LFLAGS} -o $@ $< ${OBJECTS} $(shell [ -r $(patsubst ${OBJDIR}/%.o,${SRCDIR}/%.ldflags,$<) ] && cat $(patsubst ${OBJDIR}/%.o,${SRCDIR}/%.ldflags,$<) ) && touch $@
@@ -89,6 +102,18 @@ ${OBJDIR}/%.o: ${SRCDIR}/%.cpp ${DEPDIR}/%.d Makefile
 ${OBJDIR}/%.o: ${SRCDIR}/%.c ${DEPDIR}/%.d Makefile
 	${MKDIR} ${@D} && ${MKDIR} $(patsubst ${OBJDIR}/%,${DEPDIR}/%,${@D}) && ${CC} -std=c11 -std=c17 ${CFLAGS} ${DEPFLAGS} ${LFLAGS} -o $@ -c $< && touch $@
 
+${TESTDIR}/%.ldflags:
+	touch $@
+
+${TESTBINDIR}/%: ${TESTOBJDIR}/%.o ${OBJECTS} Makefile ${TESTDIR}/%.ldflags
+	${MKDIR} ${@D} && ${MKDIR} $(patsubst ${TESTOBJDIR}/%,${DEPDIR}/%,${@D}) && ${CXX} ${CXXFLAGS} ${LFLAGS} -Itest -o $@ $< ${OBJECTS} $(shell [ -r $(patsubst ${TESTOBJDIR}/%.o,${TESTDIR}/%.ldflags,$<) ] && cat $(patsubst ${TESTOBJDIR}/%.o,${TESTDIR}/%.ldflags,$<) ) && touch $@
+
+${TESTOBJDIR}/%.o: ${TESTDIR}/%.cpp ${DEPDIR}/%.d ${DEPDIR}/test Makefile
+	${MKDIR} ${@D} && ${MKDIR} $(patsubst ${TESTOBJDIR}/%,${DEPDIR}/%,${@D}) && ${CXX} -I${TESTDIR} ${CXXFLAGS} ${DEPFLAGS} ${LFLAGS} -o $@ -c $< && touch $@
+
+${TESTOBJDIR}/%.o: ${TESTDIR}/%.c ${DEPDIR}/%.d ${DEPDIR}/test Makefile
+	${MKDIR} ${@D} && ${MKDIR} $(patsubst ${TESTOBJDIR}/%,${DEPDIR}/%,${@D}) && ${CC} -I${TESTDIR} -std=c11 -std=c17 ${CFLAGS} ${DEPFLAGS} ${LFLAGS} -o $@ -c $< && touch $@
+
 ${POTDIR}/${PODOMAIN}.pot: ${POTSRCS}
 	${MKDIR} ${@D} && ${XGETTEXT} ${XGETTEXT_FLAGS} $( [ -r $@ ] && echo -- -j ) -o $@ $^ && \
 		${SED} --expression='s/charset=CHARSET/charset=UTF-8/g' --in-place $@
@@ -106,6 +131,9 @@ ${APPDIR}/%.ldflags: ;
 
 ${DEPDIR}/%.d: ;
 
+${DEPDIR}/test:
+	${MKDIR} ${DEPDIR}/test
+
 include $(wildcard $(patsubst ${OBJDIR}/%.o,${DEPDIR}/%.d,${SRCOBJS}))
 
 translation: ${MOOBJS}
@@ -126,6 +154,12 @@ lintian:
 	lintian --pedantic --profile debian --verbose --display-experimental --show-overrides
 	@if lintian --pedantic --profile debian --verbose --display-experimental --show-overrides 2>&1 | grep -q '^W:'; then false; fi
 
+test: ${TEST_TARGETS}
+	for a in ${TEST_TARGETS}; do \
+		echo $$a; \
+		$$a; \
+	done
+
 install:
 	${INSTALL} -D -o root -g root -m 700 -t ${DESTDIR}${prefix}/bin ${TARGETS}
 	for POLANG in ${POLANGS}; do \
diff --git a/src/test/loopback_connection_client.cpp b/src/test/loopback_connection_client.cpp
deleted file mode 100644
index 2002f59e31e76ae6dda94d908d83dea31e467a74..0000000000000000000000000000000000000000
--- a/src/test/loopback_connection_client.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * loopback_connection_client.cpp
- *
- *  Created on: 05.01.2021
- *      Author: doralitze
- */
-
-#include "test/loopback_connection_client.hpp"
-
-#include <string.h>
-
-namespace rmrf::test {
-
-loopback_connection_client::loopback_connection_client(
-    rmrf::net::connection_client::incomming_data_cb mut_send_data_cb_
-) :
-    rmrf::net::connection_client{},
-    mut_send_data_cb(mut_send_data_cb_),
-    send_data_archive{}
-{
-    // Does nothing special
-}
-
-loopback_connection_client::~loopback_connection_client() {
-    // Also doesn't do anything fancy.
-}
-
-void loopback_connection_client::write_data(const std::string &data) {
-    // TODO fixme
-    this->send_data_archive.push_back(data);
-
-    if (this->mut_send_data_cb != nullptr) {
-        this->mut_send_data_cb(data);
-    }
-}
-
-void loopback_connection_client::send_data_to_incomming_data_cb(const std::string &data) {
-    if (this->in_data_cb != nullptr) {
-        this->in_data_cb(data);
-    }
-}
-
-std::vector<std::string> loopback_connection_client::get_send_data() {
-    return this->send_data_archive;
-}
-
-}
diff --git a/src/test/loopback_connection_client.hpp b/src/test/loopback_connection_client.hpp
deleted file mode 100644
index c75c678a7860e9e4e6ecb80e2ebb5d09e64dea06..0000000000000000000000000000000000000000
--- a/src/test/loopback_connection_client.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * loopback_connection_client.hpp
- *
- *  Created on: 05.01.2021
- *      Author: doralitze
- */
-
-#pragma once
-
-#include <vector>
-
-#include "net/connection_client.hpp"
-
-
-namespace rmrf::test {
-
-/**
- * Use this class to mock a connection client.
- */
-class loopback_connection_client : public rmrf::net::connection_client {
-private:
-	const rmrf::net::connection_client::incomming_data_cb mut_send_data_cb;
-	std::vector<std::string> send_data_archive;
-
-public:
-	/**
-	 * This constructor uses the given callback to notify the test suite that the module under test
-	 * send data.
-	 */
-	loopback_connection_client(rmrf::net::connection_client::incomming_data_cb mut_send_data_cb_);
-
-	/**
-	 * Just the mandatory virtual destructor.
-	 */
-	virtual ~loopback_connection_client();
-
-	/**
-	 * This method gets called by the module under test as it simulates the behavior of a normal connection client.
-	 * @param data The data the module wants to send.
-	 */
-	virtual void write_data(const std::string& data);
-
-	/**
-	 * This method sends data to the connections incoming data callback.
-	 * Use it to mimic a remote client sending data.
-	 *
-	 * @param data The data to send.
-	 */
-	void send_data_to_incomming_data_cb(const std::string& data);
-
-	/**
-	 * Use this method in order to get all data the module under test has send.
-	 */
-	std::vector<std::string> get_send_data();
-};
-
-}
diff --git a/test/loopback_connection_client.hpp b/test/loopback_connection_client.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c1ee84d57ec07c7b9821ba5223fde5f921b74f2
--- /dev/null
+++ b/test/loopback_connection_client.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+#include <vector>
+
+#include "net/connection_client.hpp"
+
+
+namespace rmrf::test {
+
+/**
+ * Use this class to mock a connection client.
+ */
+class loopback_connection_client : public rmrf::net::connection_client {
+private:
+    const rmrf::net::connection_client::incomming_data_cb send_data_cb;
+    std::vector<std::string> data_archive;
+
+public:
+    /**
+     * This constructor uses the given callback to notify the test suite that the module under test
+     * send data.
+     */
+    loopback_connection_client(
+        rmrf::net::connection_client::incomming_data_cb send_data_cb_
+    ) :
+        rmrf::net::connection_client{},
+        send_data_cb(send_data_cb_),
+        data_archive{}
+    {
+        // Does nothing special
+    }
+
+    /**
+     * Just the mandatory virtual destructor.
+     */
+    virtual ~loopback_connection_client() {
+        // Also doesn't do anything fancy.
+    }
+
+    /**
+     * This method gets called by the module under test as it simulates the behavior of a normal connection client.
+     * @param data The data the module wants to send.
+     */
+    virtual void write_data(const std::string& data) {
+        // TODO fixme
+        this->data_archive.push_back(data);
+        if (this->send_data_cb) {
+            this->send_data_cb(data);
+        }
+    }
+
+    /**
+     * This method sends data to the connections incoming data callback.
+     * Use it to mimic a remote client sending data.
+     *
+     * @param data The data to send.
+     */
+    void send_data_to_incomming_data_cb(const std::string& data) {
+        if (this->in_data_cb) {
+            this->in_data_cb(data);
+        }
+    }
+
+    /**
+     * Use this method in order to get all data the module under test has send.
+     */
+    std::vector<std::string> get_send_data() {
+        return this->data_archive;
+    }
+};
+
+}