diff --git a/.gitignore b/.gitignore
index 822a42a7..b2ea2049 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,8 +39,7 @@ htmlcov/
 .tox/
 .coverage
 .cache
-.testrepository
-nosetests.xml
+.stestr/
 coverage.xml
 
 # Translations
diff --git a/.stestr.conf b/.stestr.conf
new file mode 100644
index 00000000..6f12d78c
--- /dev/null
+++ b/.stestr.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+test_path=./senlinclient/tests/unit
+top_dir=./
diff --git a/.testr.conf b/.testr.conf
deleted file mode 100644
index fa9d34a6..00000000
--- a/.testr.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-[DEFAULT]
-test_command=${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./senlinclient/tests/unit} $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 36a0e6b5..2ee76f28 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -79,18 +79,18 @@ pytz==2013.6
 PyYAML==3.12
 reno==2.5.0
 requests==2.14.2
-requests-mock==1.1.0
+requests-mock==1.2.0
 requestsexceptions==1.2.0
 rfc3986==0.3.1
+setuptools==21.0.0
 simplejson==3.5.1
 six==1.10.0
 snowballstemmer==1.2.1
 Sphinx==1.6.5
 sphinxcontrib-websupport==1.0.1
-stestr==1.0.0
+stestr==2.0.0
 stevedore==1.20.0
 tempest==17.1.0
-testrepository==0.0.18
 testscenarios==0.4
 testtools==2.2.0
 traceback2==1.4.0
diff --git a/playbooks/legacy/senlinclient-dsvm-functional/post.yaml b/playbooks/legacy/senlinclient-dsvm-functional/post.yaml
index dac87534..22914b03 100644
--- a/playbooks/legacy/senlinclient-dsvm-functional/post.yaml
+++ b/playbooks/legacy/senlinclient-dsvm-functional/post.yaml
@@ -27,19 +27,6 @@
           - --exclude=*
           - --prune-empty-dirs
 
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.testrepository/tmp*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
     - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
       synchronize:
         src: '{{ ansible_user_dir }}/workspace/'
diff --git a/requirements.txt b/requirements.txt
index 38c983de..8de9f272 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@
 
 Babel!=2.4.0,>=2.3.4 # BSD
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
-PrettyTable<0.8,>=0.7.1 # BSD
+PrettyTable<0.8,>=0.7.2 # BSD
 keystoneauth1>=3.4.0 # Apache-2.0
 openstacksdk>=0.11.2 # Apache-2.0
 osc-lib>=1.8.0 # Apache-2.0
diff --git a/test-requirements.txt b/test-requirements.txt
index e13637b7..5818e4c7 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -12,6 +12,6 @@ python-openstackclient>=3.12.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
 setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=21.0.0 # PSF/ZPL
 tempest>=17.1.0 # Apache-2.0
-testrepository>=0.0.18 # Apache-2.0/BSD
+stestr>=2.0.0 # Apache-2.0
 testscenarios>=0.4 # Apache-2.0/BSD
 testtools>=2.2.0 # MIT
diff --git a/tox.ini b/tox.ini
index 5c879441..3f78e59c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@ deps =
        -r{toxinidir}/test-requirements.txt
 commands =
     find . -type f -name "*.py[c|o]" -delete
-    python setup.py testr --slowest --testr-args='{posargs}'
+    stestr run --slowest {posargs}
 whitelist_externals = find
 
 [testenv:pep8]
@@ -37,8 +37,13 @@ passenv = OS_*
 
 [testenv:cover]
 basepython = python3
+setenv =
+    PYTHON=coverage run --source senlinclient --parallel-mode
 commands =
-    python setup.py testr --coverage --testr-args='{posargs}'
+    stestr run '{posargs}'
+    coverage combine
+    coverage html -d cover
+    coverage xml -o cover/coverage.xml
     coverage report
 
 [testenv:debug]