diff --git a/testing/Dockerfile b/testing/Dockerfile
new file mode 100755
index 0000000000000000000000000000000000000000..e50ef7f7a1aa8c6a12e6a6b6e8c73540dcbadb85
--- /dev/null
+++ b/testing/Dockerfile
@@ -0,0 +1,23 @@
+FROM ubuntu:18.04
+ARG ZIP
+
+# Install dependencies
+RUN apt-get update && apt-get install -y python3.7
+RUN apt-get update && apt-get install -y unzip
+
+WORKDIR /usr/test
+
+COPY bibifiTests.py .
+COPY tests .
+
+COPY $ZIP  . 
+RUN sh -c 'unzip -q "Archive.zip"'
+RUN mv Archive/* ./
+RUN sh -c 'make'
+
+RUN chmod a+wrx atm
+RUN chmod a+wrx bank
+RUN chmod a+wrx bibifiTests.py
+RUN chmod a+wr core
+
+CMD ["python3", "bibifiTests.py"]
\ No newline at end of file
diff --git a/testing/bibifiTests.py b/testing/bibifiTests.py
new file mode 100755
index 0000000000000000000000000000000000000000..2043123ef384d739b2cb37cf141ec79244ea403e
--- /dev/null
+++ b/testing/bibifiTests.py
@@ -0,0 +1,138 @@
+import subprocess
+import os
+import time
+import json
+
+from shutil import copyfile
+
+"""
+Created by Oliver Wiese
+Copyright © 2020 fu-berlin.
+
+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/>.
+"""
+
+PORT = 3000
+IP = '127.0.0.1'
+
+def createBank(authFile='bank.auth', port = PORT):
+	cmd = ['./bank', '-s', authFile, '-p', str(port)]
+	result = subprocess.Popen(cmd)
+	return result, authFile
+
+def cleanup(files, accounts, server):
+	server.terminate()
+	for acc in accounts:
+		name = acc + '.card'
+		if not name in files:
+			files.append(name)
+
+	for f in files:
+		if os.path.isfile(f):
+			os.remove(f)
+
+def readTestCase(file):
+	if os.path.isfile(file):
+		fp = open(file,'r')
+		data = json.load(fp)
+		return data
+	return []
+
+def testInput(input, output):
+	args = [w.replace('%PORT%', str(PORT)) for w in input]
+	args = [w.replace('%IP%', IP) for w in args]
+	args = ['./atm'] + args
+	atm = subprocess.run(args, stdout=subprocess.PIPE)
+	res = atm.stdout.decode('utf-8')
+	if len(res) > 0:
+		res = json.loads(res)
+	else:
+		res = {}
+	equal = (res == output)
+	return equal, res, output, args
+
+def runTestCase(file):
+	trace = []
+	data = readTestCase(file)
+	error = False
+	msg = 'OK'
+	accounts = []
+	cards = []
+	if data == []:
+		return (True, [], "No test case error.")
+	bank, authFile = createBank()
+	time.sleep(1)
+	cards.append(authFile)
+	"""
+		Example: 
+		{"inputs": [
+			{"input": {"input": ["-p", "%PORT%", "-i", "%IP%", "-a", "ted", "-n", "10.30"]}, "output": {"output": {"initial_balance": 10.3, "account": "ted"}, "exit": 0}},
+			{"input": {"input": ["-p", "%PORT%", "-i", "%IP%", "-a", "alice", "-c", "alice.card", "-n", "100.00"]}, "output": {"output": {"initial_balance": 100, "account": "alice"}, "exit": 0}}
+			]}
+	"""
+	for x in data['inputs']:
+		input = x['input']
+		# Store account names/cards
+		if '-a' in input['input']:
+			i = input['input'].index('-a')
+			accounts.append(input['input'][i+1])
+		if '-c' in input['input']:
+			i = input['input'].index('-c')
+			cards.append(input['input'][i+1])
+		output = x['output']
+		equal, res, expected, call = testInput(input['input'], output['output'])
+		trace.append((call, res,expected))
+		if not equal:
+			error = True
+			msg = 'Result does not match with expectation.'
+			break
+	cleanup(cards, accounts, bank)
+	return error, trace, msg
+
+def runTestSuite(path = '../tests/core'):
+	testedFiles = []
+	for (dirpath, dirnames, filenames) in os.walk(path):
+		for f in filenames:
+			f = path+'/'+f
+			if '.json' in f:
+				testedFiles.append(f)
+				error, trace, msg = runTestCase(f)
+				if error:
+					return (error, trace, msg)
+	return (False, [], '')
+
+def feedback(error, trace, msg):
+	print('Result:')
+	if error:
+		print('ERROR')
+		print(msg)
+		print(trace)
+		text = 'Tests failed! \n Description: \n{0} \n Trace: \n {1}'.format(msg, trace)
+		return text
+	else:
+		print('No failed tests! Looks good.')
+		return 'No failed tests! Looks good.'
+
+def clean(dir):
+	if os.path.isfile(dir):
+		os.remove(dir)
+
+def startTests(dir, move = True):
+	error, trace, msg = runTestSuite(dir+'/core')
+	if move:
+		clean(dir)
+	return feedback(error, trace, msg)
+
+dir = os.getcwd()
+msg = startTests(dir, False)
diff --git a/testing/tests/core/createaccount.json b/testing/tests/core/createaccount.json
new file mode 100644
index 0000000000000000000000000000000000000000..9ec4c77d96efdec6f8ba4fe17d3678321e4de17b
--- /dev/null
+++ b/testing/tests/core/createaccount.json
@@ -0,0 +1 @@
+{"inputs": [{"input": {"input": ["-p", "%PORT%", "-i", "%IP%", "-a", "ted", "-n", "10.30"]}, "output": {"output": {"initial_balance": 10.3, "account": "ted"}, "exit": 0}}]}