diff --git a/tests/pthread/Makefile b/tests/pthread/Makefile
index 2e2c8e2c1c84c2cf29abab63850c7b23ce57dd60..587c462f86c7d69e445c14779c0158a960c2611b 100644
--- a/tests/pthread/Makefile
+++ b/tests/pthread/Makefile
@@ -8,3 +8,6 @@ USEMODULE += posix
 USEMODULE += pthread
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread/main.c b/tests/pthread/main.c
index 605102e5c7db7293319527ac9d224197e716aa0f..e4170f55f5b0455f1b6591517627c05ad2793f6e 100644
--- a/tests/pthread/main.c
+++ b/tests/pthread/main.c
@@ -23,6 +23,9 @@
 
 #include "pthread.h"
 
+#define FACTORIAL_PARAM     (6U)
+#define FACTORIAL_EXPECTED  (720U)
+
 void *run(void *parameter) {
     size_t n = (size_t) parameter;
     size_t factorial = 1;
@@ -45,14 +48,21 @@ int main(void) {
     pthread_t th_id;
     pthread_attr_t th_attr;
 
-    size_t arg = 6;
-    printf("main parameter = %u\n", (unsigned int) arg);
+    size_t arg = FACTORIAL_PARAM;
+    printf("main: parameter = %u\n", (unsigned int) arg);
 
     pthread_attr_init(&th_attr);
     pthread_create(&th_id, &th_attr, run, (void *) arg);
     size_t res;
     pthread_join(th_id, (void **) &res);
     printf("main: factorial = %u\n", (unsigned int) res);
-    puts("main: finished");
+
+    if (res == FACTORIAL_EXPECTED) {
+        puts("SUCCESS");
+    }
+    else {
+        puts("FAILURE");
+    }
+
     return 0;
 }
diff --git a/tests/pthread/tests/01-run.py b/tests/pthread/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..f2ae587229f9cadaf511dc49d94cdcb3be0872ad
--- /dev/null
+++ b/tests/pthread/tests/01-run.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import math
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+FACTORIAL_PARAM = 6
+
+
+def testfunc(child):
+    child.expect('main: parameter = {}'.format(FACTORIAL_PARAM))
+    child.expect('pthread: parameter = {}'.format(FACTORIAL_PARAM))
+    child.expect('pthread: factorial = {}'
+                 .format(math.factorial(FACTORIAL_PARAM)))
+    child.expect('main: factorial = {}'
+                 .format(math.factorial(FACTORIAL_PARAM)))
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
diff --git a/tests/pthread_barrier/Makefile b/tests/pthread_barrier/Makefile
index bcc2e7937c731c58a29a93743f3710a70a9adabc..c0c8003e8b3ea850018287b3b5a033fbf22d860b 100644
--- a/tests/pthread_barrier/Makefile
+++ b/tests/pthread_barrier/Makefile
@@ -6,7 +6,7 @@ BOARD_BLACKLIST := arduino-mega2560 waspmote-pro arduino-uno arduino-duemilanove
 # arduino mega2560 uno duemilanove: unknown type name: clockid_t
 
 # exclude boards with insufficient memory
-BOARD_INSUFFICIENT_MEMORY := nucleo32-f031 stm32f0discovery
+BOARD_INSUFFICIENT_MEMORY := nucleo32-f031
 
 # Modules to include.
 USEMODULE += pthread
@@ -14,3 +14,6 @@ USEMODULE += random
 USEMODULE += xtimer
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_barrier/main.c b/tests/pthread_barrier/main.c
index b943bea954e01d218dc73a9923fc07118eddf5a8..83c6e47d9ce42ce1f3c81dd9d69a43b3f567a933 100644
--- a/tests/pthread_barrier/main.c
+++ b/tests/pthread_barrier/main.c
@@ -56,7 +56,7 @@ int main(void)
 {
     random_init(RAND_SEED);
 
-    puts("Start.\n");
+    puts("START\n");
     pthread_barrier_init(&barrier, NULL, NUM_CHILDREN);
 
     pthread_t children[NUM_CHILDREN];
@@ -70,6 +70,7 @@ int main(void)
     }
 
     pthread_barrier_destroy(&barrier);
-    puts("\nDone.");
+    puts("");
+    puts("SUCCESS");
     return 0;
 }
diff --git a/tests/pthread_barrier/tests/01-run.py b/tests/pthread_barrier/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..9dcab9af750218e223736f589917ef13aba348b9
--- /dev/null
+++ b/tests/pthread_barrier/tests/01-run.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def testfunc(child):
+    child.expect('START')
+    for i in range(4):
+        child.expect('Start {}'.format(i + 1))
+    child.expect('Done 2')
+    child.expect('Done 1')
+    child.expect('Done 3')
+    child.expect('Done 4')
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
diff --git a/tests/pthread_cleanup/Makefile b/tests/pthread_cleanup/Makefile
index dc485fd43c6da6c312ec585aa602f305d8a8008f..f6f6bffe96c3284fcda0314452203ba308345c51 100644
--- a/tests/pthread_cleanup/Makefile
+++ b/tests/pthread_cleanup/Makefile
@@ -7,3 +7,6 @@ BOARD_BLACKLIST := arduino-mega2560 waspmote-pro arduino-uno arduino-duemilanove
 USEMODULE += pthread
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_cleanup/main.c b/tests/pthread_cleanup/main.c
index a6ec1938967ea4ce538f59b76bf8f6592ba5e3ef..516c136d881140e4dc1bc64e611aace9bef0efcc 100644
--- a/tests/pthread_cleanup/main.c
+++ b/tests/pthread_cleanup/main.c
@@ -60,7 +60,7 @@ static void *run(void *unused) {
 }
 
 int main(void) {
-    puts("Start.");
+    puts("START");
 
     pthread_t th_id;
     pthread_create(&th_id, NULL, run, NULL);
@@ -69,6 +69,12 @@ int main(void) {
     pthread_join(th_id, (void **) &res);
 
     printf("Result: %i\n", (int) (intptr_t) res);
-    puts("Done.");
+
+    if (res == RET_EXIT) {
+        puts("SUCCESS");
+    }
+    else {
+        puts("FAILURE");
+    }
     return 0;
 }
diff --git a/tests/pthread_cleanup/tests/01-run.py b/tests/pthread_cleanup/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..91d45f5f411fe3917063f9cfc434c73b44f13a9b
--- /dev/null
+++ b/tests/pthread_cleanup/tests/01-run.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def testfunc(child):
+    child.expect('START')
+    for i in range(5):
+        child.expect_exact('<SCOPE {}{}>'
+                           .format(i + 1, ' /' if i == 4 else ''))
+
+    child.expect_exact('Cleanup: <5>')
+    child.expect_exact('</SCOPE 4>')
+    child.expect_exact('</SCOPE 3>')
+    for i in (3, 2, 1):
+        child.expect_exact('Cleanup: <{}>'.format(i))
+    child.expect_exact('Result: 1234')
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
diff --git a/tests/pthread_cleanup/tests/01-test b/tests/pthread_cleanup/tests/01-test
deleted file mode 100755
index 6e7b30b0309ed743f9a6aeb5891c77100cdfcbec..0000000000000000000000000000000000000000
--- a/tests/pthread_cleanup/tests/01-test
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env expect
-
-set timeout 5
-
-set pid [spawn make term]
-puts "-*- Spawened $pid -*-\n"
-
-set once 0
-set result 1
-while { $once == 0 } {
-    set once 1
-
-    expect {
-        "Start." {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 0>" {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 1>" {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 2>" {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 3>" {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 4>" {}
-        timeout { break }
-    }
-    expect {
-        "<SCOPE 5 />" {}
-        timeout { break }
-    }
-    expect {
-        "Cleanup: <5>" {}
-        timeout { break }
-    }
-    expect {
-        "</SCOPE 4>" {}
-        timeout { break }
-    }
-    # Cleanup 4 has execute == 0.
-    expect {
-        "</SCOPE 3>" {}
-        timeout { break }
-    }
-    # pthread_exit is called  here
-    expect {
-        "Cleanup: <3>" {}
-        timeout { break }
-    }
-    expect {
-        "Cleanup: <2>" {}
-        timeout { break }
-    }
-    expect {
-        "Cleanup: <1>" {}
-        timeout { break }
-    }
-    expect {
-        "Result: 1234" {}
-        timeout { break }
-    }
-    expect {
-        "Done." {}
-        timeout { break }
-    }
-
-    set result 0
-}
-
-if { $result == 0 } {
-    puts "\n-*- Test successful! -*-\n"
-} else {
-    puts "\n-*- TEST HAD ERRORS! -*-\n"
-}
-spawn kill -9 $pid
-wait
-close
-exit $result
diff --git a/tests/pthread_condition_variable/Makefile b/tests/pthread_condition_variable/Makefile
index d56deac6d0729f4060eb66f632d121358d803eb7..eafee2377a501c07392644faa81ff42d37da12c0 100644
--- a/tests/pthread_condition_variable/Makefile
+++ b/tests/pthread_condition_variable/Makefile
@@ -4,7 +4,7 @@ include ../Makefile.tests_common
 BOARD_BLACKLIST := arduino-mega2560 waspmote-pro arduino-uno arduino-duemilanove
 # arduino mega2560 uno duemilanove: unknown type name: clockid_t
 
-BOARD_INSUFFICIENT_MEMORY := nucleo32-f031 stm32f0discovery
+BOARD_INSUFFICIENT_MEMORY := nucleo32-f031
 
 USEMODULE += posix
 USEMODULE += pthread
@@ -13,3 +13,6 @@ USEMODULE += xtimer
 CFLAGS += -DNATIVE_AUTO_EXIT
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_condition_variable/main.c b/tests/pthread_condition_variable/main.c
index 75bc36f7348346e80934e1a7a525ca8ccfe6300d..e16b6ef6ca8cd4bf6fce7f58798230ec4f1d93d8 100644
--- a/tests/pthread_condition_variable/main.c
+++ b/tests/pthread_condition_variable/main.c
@@ -52,6 +52,7 @@ static void *second_thread(void *arg)
 
 int main(void)
 {
+    puts("START");
     count = 0;
     is_finished = 0;
     expected_value = 1000ul * 1000ul;
@@ -74,10 +75,14 @@ int main(void)
             puts("condition fulfilled.");
             is_finished = 1;
             mutex_unlock(&mutex);
-            return 0;
+            break;
         }
 
         pthread_cond_wait(&cv, &mutex);
         mutex_unlock(&mutex);
     }
+
+    puts("SUCCESS");
+
+    return 0;
 }
diff --git a/tests/pthread_condition_variable/tests/01-run.py b/tests/pthread_condition_variable/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..a88053e6fbeb2c40fa76bbb761253e72f9b95584
--- /dev/null
+++ b/tests/pthread_condition_variable/tests/01-run.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def testfunc(child):
+    child.expect('START')
+    child.expect('condition fulfilled.')
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    # This test can take some time to complete when testing on hardware (e.g
+    # on samr21-xpro) and the default timeout (10s) is not enough.
+    sys.exit(testrunner.run(testfunc, timeout=60))
diff --git a/tests/pthread_cooperation/Makefile b/tests/pthread_cooperation/Makefile
index 22b54c561188668fe64b302b995b355a591b89ee..cfb4ee6c8b1caf279a3e28580c658108776cdfba 100644
--- a/tests/pthread_cooperation/Makefile
+++ b/tests/pthread_cooperation/Makefile
@@ -10,3 +10,6 @@ USEMODULE += posix
 USEMODULE += pthread
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_cooperation/main.c b/tests/pthread_cooperation/main.c
index b0115965812fb1af7b12153b127c388db9fa9b0e..8ab28ad522abc5b730ea28d2275fed8467f31083 100644
--- a/tests/pthread_cooperation/main.c
+++ b/tests/pthread_cooperation/main.c
@@ -23,6 +23,8 @@
 
 #define NUM_THREADS 12
 
+#define FACTORIAL_EXPECTED (479001600UL)
+
 pthread_t ths[NUM_THREADS];
 
 pthread_mutex_t mtx;
@@ -49,6 +51,7 @@ void *run(void *parameter)
 
 int main(void)
 {
+    puts("START");
     pthread_attr_t th_attr;
     pthread_attr_init(&th_attr);
     pthread_mutex_init(&mtx, NULL);
@@ -59,19 +62,21 @@ int main(void)
     }
 
     for (int i = 0; i < NUM_THREADS; ++i) {
-        printf("join\n");
+        printf("join thread %d\n", (i + 1));
         pthread_join(ths[i], NULL);
     }
 
     printf("Factorial: %d\n", storage);
 
-    if (storage != 479001600) {
-        puts("[!!!] Error, expected: 12!= 479001600.");
-    }
-
     pthread_mutex_destroy(&mtx);
     pthread_attr_destroy(&th_attr);
 
-    puts("finished");
+    if (storage == FACTORIAL_EXPECTED) {
+        puts("SUCCESS");
+    }
+    else {
+        puts("FAILURE: Error, expected: 12!= 479001600.");
+    }
+
     return 0;
 }
diff --git a/tests/pthread_cooperation/tests/01-run.py b/tests/pthread_cooperation/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..289259d3e1e2ccd36f939a2d57053331803d14b8
--- /dev/null
+++ b/tests/pthread_cooperation/tests/01-run.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def testfunc(child):
+    child.expect('START')
+
+    for i in range(12):
+        child.expect('Creating thread with arg {}'.format(i + 1))
+
+    for i in range(12):
+        child.expect('join thread {}'.format(i + 1))
+
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
diff --git a/tests/pthread_rwlock/Makefile b/tests/pthread_rwlock/Makefile
index 6ed396f2603e95d533a549dbdc81bfdb618192c1..1216ed0e4eea23e066118b4e9978f2d429834e25 100644
--- a/tests/pthread_rwlock/Makefile
+++ b/tests/pthread_rwlock/Makefile
@@ -8,11 +8,11 @@ USEMODULE += pthread
 USEMODULE += xtimer
 USEMODULE += random
 
-CFLAGS += -DNATIVE_AUTO_EXIT
-
-BOARD_INSUFFICIENT_MEMORY += airfy-beacon chronos mbed_lpc1768 msb-430 msb-430h \
-                             nrf51dongle nrf6310 nucleo32-f031 nucleo32-f042 nucleo32-l031 \
-                             nucleo-f030 nucleo-f334 nucleo-l053 pca10000 pca10005 \
-                             spark-core stm32f0discovery yunjia-nrf51822
+BOARD_INSUFFICIENT_MEMORY += chronos msb-430 msb-430h nucleo32-f031 \
+                             nucleo32-f042 nucleo32-l031 nucleo-f030 \
+                             nucleo-f334 nucleo-l053 stm32f0discovery
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_rwlock/main.c b/tests/pthread_rwlock/main.c
index 5635062e23ae1ae4bc5e973d5e7cd947d7b1012b..3955841f63d7960f688ec326b1a58c11de62c875 100644
--- a/tests/pthread_rwlock/main.c
+++ b/tests/pthread_rwlock/main.c
@@ -53,21 +53,32 @@
 static pthread_rwlock_t rwlock;
 static volatile unsigned counter;
 
-#define PRINTF(FMT, ...) \
-    printf("%c%" PRIkernel_pid " (prio=%u): " FMT "\n", __func__[0], sched_active_pid, sched_active_thread->priority, __VA_ARGS__)
+static kernel_pid_t main_thread_pid;
+
+#define PRINTF(FMT, ...)                                \
+    printf("%c%" PRIkernel_pid " (prio=%u): " FMT "\n", \
+           __func__[0], sched_active_pid,               \
+           sched_active_thread->priority,               \
+           (int)__VA_ARGS__)
+
+static void _notify_main_thread(void)
+{
+    msg_t msg;
+    msg_send(&msg, main_thread_pid);
+}
 
 static void do_sleep(int factor)
 {
     uint32_t timeout_us = (random_uint32() % 100000) * factor;
-    /* PRINTF("sleep for % 8i µs.", timeout_us); */
+    PRINTF("sleep for % 8i µs.", timeout_us);
     xtimer_usleep(timeout_us);
 }
 
 static void *writer(void *arg)
 {
     (void) arg;
-    /* PRINTF("%s", "start"); */
-    for (int i = 0; i < NUM_ITERATIONS; ++i) {
+    puts("start");
+    for (unsigned i = 0; i < NUM_ITERATIONS; ++i) {
         pthread_rwlock_wrlock(&rwlock);
         unsigned cur = ++counter;
         do_sleep(3); /* simulate time that it takes to write the value */
@@ -75,15 +86,16 @@ static void *writer(void *arg)
         pthread_rwlock_unlock(&rwlock);
         do_sleep(2);
     }
-    /* PRINTF("%s", "done"); */
+    puts("done");
+    _notify_main_thread();
     return NULL;
 }
 
 static void *reader(void *arg)
 {
     (void) arg;
-    /* PRINTF("%s", "start"); */
-    for (int i = 0; i < NUM_ITERATIONS; ++i) {
+    puts("start");
+    for (unsigned i = 0; i < NUM_ITERATIONS; ++i) {
         pthread_rwlock_rdlock(&rwlock);
         unsigned cur = counter;
         do_sleep(1); /* simulate time that it takes to read the value */
@@ -91,7 +103,9 @@ static void *reader(void *arg)
         pthread_rwlock_unlock(&rwlock);
         do_sleep(1);
     }
-    /* PRINTF("%s", "done"); */
+    puts("done");
+    _notify_main_thread();
+
     return NULL;
 }
 
@@ -99,7 +113,9 @@ int main(void)
 {
     static char stacks[NUM_CHILDREN][THREAD_STACKSIZE_MAIN];
 
-    puts("Main start.");
+    puts("START");
+    /* Get main thread pid */
+    main_thread_pid = thread_getpid();
 
     for (unsigned i = 0; i < NUM_CHILDREN; ++i) {
         int prio;
@@ -132,6 +148,13 @@ int main(void)
                       fun, NULL, name);
     }
 
-    puts("Main done.");
+    /* Block until all children threads are done */
+    for (unsigned i = 0; i < NUM_CHILDREN; ++i) {
+        msg_t msg;
+        msg_receive(&msg);
+    }
+
+    puts("SUCCESS");
+
     return 0;
 }
diff --git a/tests/pthread_rwlock/tests/01-run.py b/tests/pthread_rwlock/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..260c4492639233c4d94d34ba148ad9ca81d0f184
--- /dev/null
+++ b/tests/pthread_rwlock/tests/01-run.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def testfunc(child):
+    child.expect('START')
+    for _ in range(8):
+        child.expect('start')
+
+    for _ in range(8):
+        child.expect('done')
+
+    child.expect('SUCCESS')
+
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
diff --git a/tests/pthread_tls/Makefile b/tests/pthread_tls/Makefile
index 0fe7b4e5a836c8c6ead77a158a74aedac2ba981d..65cc54b1d7a4876c76cba3c5c7b226f6665b78c9 100644
--- a/tests/pthread_tls/Makefile
+++ b/tests/pthread_tls/Makefile
@@ -8,3 +8,6 @@ USEMODULE += posix
 USEMODULE += pthread
 
 include $(RIOTBASE)/Makefile.include
+
+test:
+	tests/01-run.py
diff --git a/tests/pthread_tls/main.c b/tests/pthread_tls/main.c
index 2d117bbadc4697b4065f333e07c48f6992b72896..0ed46848a5c7bec0d5bce708d808f6b90aed0a5c 100644
--- a/tests/pthread_tls/main.c
+++ b/tests/pthread_tls/main.c
@@ -30,7 +30,8 @@ void *run(void *parameter)
 
     (void)parameter;
 
-    printf("\n-= TEST 1 - create %d tls with sequencial values 0...%d =-\n", NUMBER_OF_TLS, NUMBER_OF_TLS - 1);
+    printf("\n-= TEST 1 - create %d tls with sequencial values 0...%d =-\n",
+           NUMBER_OF_TLS, NUMBER_OF_TLS - 1);
 
     for (int i = 0; i < NUMBER_OF_TLS; ++i) {
         aTLS_values[i] = i;
@@ -44,11 +45,12 @@ void *run(void *parameter)
         aTLS_values[i]++;
     }
 
-    printf("pick deliberate storage (key[3]:%d) and change the value\n", (int)aKeys[3]);
+    printf("pick deliberate storage (key[3]:%d) and change the value\n",
+           (int)aKeys[3]);
     void *val = pthread_getspecific(aKeys[3]);
     *((int *)val) = 42;
 
-    printf("show tls values:\n");
+    puts("show tls values:");
 
     for (int i = 0; i < NUMBER_OF_TLS; ++i) {
         void *val = pthread_getspecific(aKeys[i]);
@@ -56,10 +58,11 @@ void *run(void *parameter)
         printf("key[%d]: %d, val: %d\n",i, (int)aKeys[i], x);
     }
 
-    printf("\n -= TEST 2 - delete deliberate key (key[5]:%d) =-\n", (int)aKeys[5]);
+    printf("\n -= TEST 2 - delete deliberate key (key[5]:%d) =-\n",
+           (int)aKeys[5]);
     pthread_key_delete(aKeys[5]);
 
-    printf("show tls values:\n");
+    puts("show tls values:");
 
     for (int i = 0; i < NUMBER_OF_TLS; ++i) {
         void *val = pthread_getspecific(aKeys[i]);
@@ -70,7 +73,8 @@ void *run(void *parameter)
         }
     }
 
-    printf("\n-= TEST 3 - create new tls =-\n");
+    puts("");
+    puts("-= TEST 3 - create new tls =-");
     int new_val = 99;
     pthread_key_t new_key;
     pthread_key_create(&new_key, NULL);
@@ -88,7 +92,8 @@ void *run(void *parameter)
         }
     }
 
-    printf("\n-= TEST 4 - delete all keys =-\n");
+    puts("");
+    puts("-= TEST 4 - delete all keys =-");
 
     for (int i = 0; i < NUMBER_OF_TLS; ++i) {
         pthread_key_delete(aKeys[i]);
@@ -105,17 +110,22 @@ void *run(void *parameter)
         }
     }
 
-    printf("\n-= TEST 5 - try delete non-existing key =-\n");
-    printf("try to delete returns: %d\n", pthread_key_delete((pthread_key_t)99));
+    puts("");
+    puts("-= TEST 5 - try delete non-existing key =-");
+    printf("try to delete returns: %d\n",
+           pthread_key_delete((pthread_key_t)99));
+
+    puts("");
+    puts("-= TEST 6 - add key and delete without a tls =-");
 
-    printf("\n-= TEST 6 - add key and delete without a tls =-\n");
     pthread_key_create(&new_key, NULL);
-    printf("crated key: %d\n", (int)new_key);
+    printf("created key: %d\n", (int)new_key);
     printf("try to delete returns: %d\n", pthread_key_delete(new_key));
 
-    printf("\n-= TEST 7 - add key without tls =-\n");
+    puts("");
+    puts("-= TEST 7 - add key without tls =-");
     pthread_key_create(&new_key, NULL);
-    printf("crated key: %d\n", (int)new_key);
+    printf("created key: %d\n", (int)new_key);
     void* test_7_val = pthread_getspecific(new_key);
     printf("test_7_val: %p\n", test_7_val);
 
@@ -125,6 +135,7 @@ void *run(void *parameter)
 
 int main(void)
 {
+    puts("START");
     pthread_t th_id;
     pthread_attr_t th_attr;
 
@@ -133,6 +144,13 @@ int main(void)
 
     size_t res;
     pthread_join(th_id, (void **) &res);
-    puts("\ntls tests finished.");
+    puts("tls tests finished.");
+
+    if (res == 0) {
+        puts("SUCCESS");
+    }
+    else {
+        puts("FAILURE");
+    }
     return 0;
 }
diff --git a/tests/pthread_tls/tests/01-run.py b/tests/pthread_tls/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..12435e7b908a19d808511298364976eb12325772
--- /dev/null
+++ b/tests/pthread_tls/tests/01-run.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+
+def _check_test_output(child):
+    child.expect('show tls values:')
+    for i in range(20):
+        if i != 5:
+            child.expect('key\[%d\]: \d+, val: %d'
+                         % (i, i + 1 if i != 3 else 42))
+
+
+def testfunc(child):
+    child.expect('START')
+    child.expect('-= TEST 1 - create 20 tls with sequencial values 0...19 =-')
+    _check_test_output(child)
+    child.expect('-= TEST 2 - '
+                 'delete deliberate key \(key\[5\]:\d+\) =-')
+    _check_test_output(child)
+    child.expect('-= TEST 3 - create new tls =-')
+    _check_test_output(child)
+    child.expect('-= TEST 4 - delete all keys =-')
+    child.expect('show tls values:')
+    child.expect('-= TEST 5 - try delete non-existing key =-')
+    child.expect('try to delete returns: 0')
+    child.expect('-= TEST 6 - add key and delete without a tls =-')
+    child.expect('created key: \d+')
+    child.expect('try to delete returns: 0')
+    child.expect('-= TEST 7 - add key without tls =-')
+    child.expect('created key: \d+')
+    child.expect('test_7_val: (0|\(nil\))')
+    child.expect('tls tests finished.')
+    child.expect('SUCCESS')
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))