You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2020/04/23 06:54:36 UTC

[incubator-mxnet] branch master updated: [DEV] switch nose with pytest (#18025)

This is an automated email from the ASF dual-hosted git repository.

zhasheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/master by this push:
     new faccd91  [DEV] switch nose with pytest (#18025)
faccd91 is described below

commit faccd91071cc34ed0b3a192d3c7932441fe7e35e
Author: Sheng Zha <sz...@users.noreply.github.com>
AuthorDate: Wed Apr 22 23:53:12 2020 -0700

    [DEV] switch nose with pytest (#18025)
    
    * switch nose with pytest
    
    * switch centos python to 3.6
    
    * disable dist kvstore tests
    
    * skip hanging test
---
 .github/workflows/os_x_staticbuild.yml             |   6 +-
 3rdparty/mshadow/mshadow/random.h                  |   6 +-
 benchmark/opperf/opperf.py                         |   0
 cd/python/docker/Dockerfile.test                   |   2 +-
 cd/python/pypi/pypi_publish.py                     |   0
 cd/utils/artifact_repository.py                    |   0
 ci/dev_menu.py                                     |   6 +-
 ci/docker/Dockerfile.build.centos7                 |  15 +-
 ci/docker/Dockerfile.build.test.armv7              |  15 +-
 ci/docker/Dockerfile.build.test.armv8              |  13 +-
 ci/docker/Dockerfile.build.ubuntu_cpu              |   6 -
 ci/docker/Dockerfile.build.ubuntu_cpu_julia        |   6 -
 ci/docker/Dockerfile.build.ubuntu_cpu_python       |   5 +-
 ci/docker/Dockerfile.build.ubuntu_cpu_scala        |   2 +-
 ci/docker/Dockerfile.build.ubuntu_gpu_cu101        |   6 -
 ci/docker/Dockerfile.build.ubuntu_nightly_cpu      |   6 -
 ci/docker/Dockerfile.build.ubuntu_nightly_gpu      |   6 -
 ci/docker/install/requirements                     |  41 +++-
 ci/docker/install/ubuntu_caffe.sh                  |  58 ------
 ci/docker/install/ubuntu_onnx.sh                   |  34 ----
 ci/docker/install/ubuntu_python.sh                 |   1 +
 ci/docker/runtime_functions.sh                     | 143 +++++--------
 ci/docker_cache.py                                 |   0
 ci/docker_login.py                                 |   0
 ci/jenkins/Jenkins_steps.groovy                    |  67 +++---
 ci/jenkins/Jenkinsfile_centos_cpu                  |   2 +-
 ci/jenkins/Jenkinsfile_centos_gpu                  |   2 +-
 ci/jenkins/Jenkinsfile_clang                       |   2 +-
 ci/jenkins/Jenkinsfile_edge                        |   2 +-
 ci/jenkins/Jenkinsfile_miscellaneous               |   2 +-
 ci/jenkins/Jenkinsfile_sanity                      |   2 +-
 ci/jenkins/Jenkinsfile_tools                       |   2 +-
 ci/jenkins/Jenkinsfile_unix_gpu                    |   8 +-
 ci/jenkins/Jenkinsfile_website_beta                |   2 +-
 ci/jenkins/Jenkinsfile_website_full                |   2 +-
 ci/jenkins/Jenkinsfile_website_full_pr             |   2 +-
 ci/jenkins/Jenkinsfile_website_mxnet_build         |   2 +-
 ci/jenkins/Jenkinsfile_website_nightly             |   2 +-
 ci/jenkins/Jenkinsfile_windows_cpu                 |   2 +-
 ci/jenkins/Jenkinsfile_windows_gpu                 |   2 +-
 ci/safe_docker_run.py                              |   0
 ci/test_docker_cache.py                            |   5 -
 ci/test_docker_login.py                            |   4 -
 ci/test_safe_docker_run.py                         |   5 -
 ci/windows/test_py3_cpu.ps1                        |   8 +-
 ci/windows/test_py3_gpu.ps1                        |  14 +-
 conftest.py                                        | 226 +++++++++++++++++++++
 .../examples/captcha/gen_captcha.py                |   0
 docker/install/python.sh                           |   7 +-
 .../src/pages/get_started/build_from_source.md     |   2 +-
 example/image-classification/__init__.py           |   0
 example/image-classification/benchmark.py          |   0
 example/image-classification/benchmark_score.py    |   0
 example/image-classification/common/data.py        |   0
 example/image-classification/common/fit.py         |   0
 example/image-classification/fine-tune.py          |   0
 example/image-classification/score.py              |   0
 example/image-classification/symbols/alexnet.py    |   0
 example/image-classification/symbols/resnet-v1.py  |   0
 example/image-classification/symbols/resnetv1.py   |   0
 example/image-classification/test_score.py         |  34 ++--
 example/image-classification/train_cifar10.py      |   0
 example/image-classification/train_imagenet.py     |   0
 example/image-classification/train_mnist.py        |   0
 example/neural_collaborative_filtering/ci.py       |   8 +-
 example/reinforcement-learning/dqn/dqn_demo.py     |   0
 example/reinforcement-learning/dqn/dqn_run_test.py |   0
 example/ssd/data/demo/download_demo_images.py      |   0
 example/ssd/dataset/pycocotools/__init__.py        |   0
 example/ssd/dataset/pycocotools/coco.py            |   0
 example/ssd/demo.py                                |   0
 example/ssd/tools/prepare_dataset.py               |   0
 example/ssd/train.py                               |   0
 docker/install/python.sh => pytest.ini             |  20 +-
 python/README.md                                   |  10 +-
 python/mxnet/_ffi/base.py                          |  15 --
 python/mxnet/contrib/amp/amp.py                    |   0
 python/mxnet/contrib/amp/loss_scaler.py            |   0
 python/mxnet/image/detection.py                    |   4 +-
 python/mxnet/initializer.py                        |   0
 python/mxnet/module/executor_group.py              |   0
 python/mxnet/optimizer/optimizer.py                |   0
 python/mxnet/test_utils.py                         |  18 --
 python/setup.py                                    |   2 +-
 snapcraft.yaml                                     |  79 -------
 tests/README.md                                    |   2 +-
 tests/jenkins/run_test.sh                          |  14 +-
 tests/jenkins/run_test_amzn_linux_gpu.sh           |   8 +-
 tests/jenkins/run_test_ubuntu.sh                   |  14 +-
 .../broken_link_checker_test/test_broken_links.py  |   0
 .../nightly/compilation_warnings/process_output.py |   0
 tests/nightly/dist_device_sync_kvstore.py          |   3 +-
 tests/nightly/estimator/test_estimator_cnn.py      |   4 -
 tests/nightly/estimator/test_sentiment_rnn.py      |   4 -
 tests/nightly/test_distributed_training-gpu.sh     |   6 +-
 tests/nightly/test_large_array.py                  |  21 +-
 tests/nightly/test_large_vector.py                 |   7 +-
 tests/nightly/test_np_random.py                    |   8 +-
 tests/nightly/test_optimizer.py                    |   3 -
 tests/python/gpu/test_contrib_amp.py               |  38 ++--
 tests/python/gpu/test_deferred_compute_gpu.py      |   6 +-
 tests/python/gpu/test_forward.py                   |   2 +-
 tests/python/gpu/test_fusion.py                    |   3 -
 tests/python/gpu/test_gluon_contrib_gpu.py         |   3 -
 tests/python/gpu/test_gluon_gpu.py                 |   9 +-
 tests/python/gpu/test_gluon_model_zoo_gpu.py       | 101 +++++----
 tests/python/gpu/test_gluon_transforms.py          |   2 +-
 tests/python/gpu/test_kvstore_gpu.py               |   9 +-
 tests/python/gpu/test_numpy_fallback.py            |   5 -
 tests/python/gpu/test_operator_gpu.py              |  14 +-
 tests/python/gpu/test_predictor.py                 |   6 +-
 tests/python/gpu/test_tvm_bridge.py                |   4 -
 .../test_tvm_op_gpu.py}                            |  17 +-
 tests/python/mkl/test_bf16_operator.py             |  12 +-
 tests/python/mkl/test_contrib_amp.py               |  14 +-
 tests/python/mkl/test_mkldnn.py                    |   3 -
 tests/python/mkl/test_quantization_mkldnn.py       |   4 +-
 tests/python/mkl/test_subgraph.py                  |   4 -
 tests/python/profiling/test_nvtx.py                |   4 -
 tests/python/quantization/test_quantization.py     |   4 -
 .../quantization_gpu/test_quantization_gpu.py      |   5 -
 tests/python/tensorrt/lenet5_train.py              |   0
 tests/python/tensorrt/test_cvnets.py               |   5 -
 tests/python/tensorrt/test_ops.py                  |   3 -
 tests/python/tensorrt/test_resnet18.py             |   3 -
 tests/python/tensorrt/test_tensorrt_lenet5.py      |   3 -
 tests/python/train/test_conv.py                    |   0
 tests/python/unittest/common.py                    |  59 ++++--
 .../unittest}/onnx/README.md                       |   0
 .../unittest}/onnx/backend.py                      |   0
 .../unittest}/onnx/backend_rep.py                  |   0
 .../unittest}/onnx/backend_test.py                 |   4 +-
 .../unittest}/onnx/mxnet_export_test.py            |  19 +-
 .../unittest}/onnx/test_cases.py                   |   0
 .../unittest}/onnx/test_models.py                  | 128 ++++++------
 .../unittest}/onnx/test_node.py                    |  23 ++-
 tests/python/unittest/test_autograd.py             |   6 +-
 tests/python/unittest/test_base.py                 |   2 -
 tests/python/unittest/test_contrib_autograd.py     |   6 +-
 tests/python/unittest/test_contrib_control_flow.py |  11 +-
 tests/python/unittest/test_contrib_hawkesll.py     |   5 -
 tests/python/unittest/test_contrib_operator.py     |   4 -
 tests/python/unittest/test_contrib_optimizer.py    |   3 -
 tests/python/unittest/test_contrib_stes_op.py      |   3 -
 tests/python/unittest/test_contrib_svrg_module.py  |   4 -
 .../python/unittest/test_contrib_svrg_optimizer.py |   4 -
 tests/python/unittest/test_contrib_text.py         |   4 -
 tests/python/unittest/test_deferred_compute.py     |  18 +-
 tests/python/unittest/test_dgl_graph.py            |   3 -
 tests/python/unittest/test_dlpack.py               |   3 -
 tests/python/unittest/test_dynamic_shape.py        |   4 -
 tests/python/unittest/test_engine.py               |   6 -
 tests/python/unittest/test_engine_import.py        |   6 +-
 tests/python/unittest/test_exc_handling.py         |  38 ++--
 tests/python/unittest/test_executor.py             |   6 +-
 tests/python/unittest/test_gluon.py                |  39 ++--
 .../python/unittest/test_gluon_batch_processor.py  |  10 +-
 tests/python/unittest/test_gluon_contrib.py        |  12 +-
 tests/python/unittest/test_gluon_data.py           |   6 +-
 tests/python/unittest/test_gluon_data_vision.py    |  17 +-
 tests/python/unittest/test_gluon_estimator.py      |  24 +--
 tests/python/unittest/test_gluon_event_handler.py  |  16 +-
 tests/python/unittest/test_gluon_model_zoo.py      |   7 +-
 tests/python/unittest/test_gluon_rnn.py            |   4 -
 tests/python/unittest/test_gluon_trainer.py        |  10 +-
 tests/python/unittest/test_gluon_utils.py          |   4 +-
 tests/python/unittest/test_higher_order_grad.py    |  30 ++-
 tests/python/unittest/test_image.py                |  99 +++++----
 tests/python/unittest/test_infer_shape.py          |   7 +-
 tests/python/unittest/test_infer_type.py           |   5 -
 tests/python/unittest/test_io.py                   |  23 +--
 tests/python/unittest/test_kvstore.py              |   8 +-
 tests/python/unittest/test_kvstore_custom.py       |   5 +-
 tests/python/unittest/test_loss.py                 |  10 +-
 tests/python/unittest/test_metric.py               |   4 -
 tests/python/unittest/test_metric_perf.py          |   4 -
 tests/python/unittest/test_module.py               |   6 +-
 tests/python/unittest/test_ndarray.py              |  16 +-
 tests/python/unittest/test_numpy_gluon.py          |   4 -
 .../python/unittest/test_numpy_interoperability.py |  12 +-
 tests/python/unittest/test_numpy_ndarray.py        |  13 +-
 tests/python/unittest/test_numpy_op.py             |  43 ++--
 tests/python/unittest/test_operator.py             |  41 ++--
 tests/python/unittest/test_optimizer.py            |  13 +-
 tests/python/unittest/test_predictor.py            |   6 +-
 tests/python/unittest/test_profiler.py             |   8 +-
 tests/python/unittest/test_random.py               |  94 ++++-----
 tests/python/unittest/test_recordio.py             |   2 +-
 tests/python/unittest/test_rnn.py                  |   5 +-
 tests/python/unittest/test_runtime.py              |  16 +-
 tests/python/unittest/test_sparse_ndarray.py       |   5 +-
 tests/python/unittest/test_sparse_operator.py      |   5 +-
 tests/python/unittest/test_subgraph.py             |   5 +-
 tests/python/unittest/test_subgraph_op.py          |  19 +-
 tests/python/unittest/test_symbol.py               |  11 +-
 tests/python/unittest/test_test_utils.py           |   7 +-
 tests/python/unittest/test_thread_local.py         |   4 -
 tests/python/unittest/test_tvm_op.py               |   3 -
 tests/python/unittest/test_viz.py                  |   3 -
 tests/requirements.txt                             |   8 -
 tools/caffe_converter/test_converter.py            |   0
 tools/dependencies/README.md                       |   2 +-
 tools/diagnose.py                                  |   0
 tools/flakiness_checker.py                         |  20 +-
 tools/im2rec.py                                    |   0
 tools/ipynb2md.py                                  |   0
 tools/launch.py                                    |   0
 tools/parse_log.py                                 |   0
 208 files changed, 1032 insertions(+), 1370 deletions(-)

diff --git a/.github/workflows/os_x_staticbuild.yml b/.github/workflows/os_x_staticbuild.yml
index eb7bc0b..6727b22 100644
--- a/.github/workflows/os_x_staticbuild.yml
+++ b/.github/workflows/os_x_staticbuild.yml
@@ -10,7 +10,8 @@ jobs:
         uses: actions/checkout@v2
       - name: Install Dependencies
         run: |
-          brew install nasm automake ninja libtool cmake pkgconfig protobuf
+          brew install nasm automake ninja libtool cmake pkgconfig protobuf hdf5 zlib
+          python3 -m pip install --user -r ci/docker/install/requirements
       - name: Build project
         run: |
           git --version
@@ -18,8 +19,7 @@ jobs:
           CMAKE_STATICBUILD=1 ./tools/staticbuild/build.sh cpu
       - name: Setup Python
         run: |
-          python3 -m pip install --user nose nose-timer nose-exclude numpy scipy
           python3 -m pip install --user -e python
       - name: Test project
         run: |
-          python3 -m nose --with-timer --verbose tests/python/unittest/ --exclude-test=test_extensions.test_subgraph --exclude-test=test_extensions.test_custom_op --exclude-test=test_gluon_data.test_recordimage_dataset_with_data_loader_multiworker --exclude-test=test_gluon_data.test_multi_worker --exclude-test=test_gluon_data.test_multi_worker_shape --exclude-test=test_gluon_data.test_multi_worker_forked_data_loader --exclude-test=test_gluon_data.test_multi_worker_dataloader_release_pool
+          python3 -m pytest --durations=50 --verbose tests/python/unittest/ -k 'not (test_subgraph or test_custom_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)'
diff --git a/3rdparty/mshadow/mshadow/random.h b/3rdparty/mshadow/mshadow/random.h
index 13c3405..95f17ba 100644
--- a/3rdparty/mshadow/mshadow/random.h
+++ b/3rdparty/mshadow/mshadow/random.h
@@ -336,8 +336,10 @@ class Random<gpu, DType> {
    * \brief get a set of random integers
    */
   inline void GetRandInt(const Tensor<gpu, 1, unsigned>& dst) {
-    curandStatus_t status = curandGenerate(gen_, dst.dptr_, dst.size(0));
-    CHECK_EQ(status, CURAND_STATUS_SUCCESS) << "CURAND Gen rand ints failed.";
+    curandStatus_t status;
+    status = curandGenerate(gen_, dst.dptr_, dst.size(0));
+    CHECK_EQ(status, CURAND_STATUS_SUCCESS) << "CURAND Gen rand ints failed."
+                                            << " size = " << dst.size(0);
   }
   /*!
    * \brief generate data from uniform [a,b)
diff --git a/benchmark/opperf/opperf.py b/benchmark/opperf/opperf.py
old mode 100755
new mode 100644
diff --git a/cd/python/docker/Dockerfile.test b/cd/python/docker/Dockerfile.test
index bed059d..6ded5f4 100644
--- a/cd/python/docker/Dockerfile.test
+++ b/cd/python/docker/Dockerfile.test
@@ -24,7 +24,7 @@ ARG BASE_IMAGE
 FROM ${BASE_IMAGE}
 
 # Install test dependencies
-RUN pip install nose
+RUN pip install pytest
 
 ARG USER_ID=1001
 ARG GROUP_ID=1001
diff --git a/cd/python/pypi/pypi_publish.py b/cd/python/pypi/pypi_publish.py
old mode 100755
new mode 100644
diff --git a/cd/utils/artifact_repository.py b/cd/utils/artifact_repository.py
old mode 100755
new mode 100644
diff --git a/ci/dev_menu.py b/ci/dev_menu.py
old mode 100755
new mode 100644
index 962e4ec..365d2b8
--- a/ci/dev_menu.py
+++ b/ci/dev_menu.py
@@ -105,8 +105,8 @@ def provision_virtualenv(venv_path=DEFAULT_PYENV):
         # Install MXNet python bindigs
         check_call([pip, 'install', '--upgrade', '--force-reinstall', '-e', 'python'])
         # Install test dependencies
-        check_call([pip, 'install', '--upgrade', '--force-reinstall', '-r', os.path.join('tests',
-            'requirements.txt')])
+        check_call([pip, 'install', '--upgrade', '--force-reinstall', '-r',
+                    os.path.join('ci', 'docker', 'install', 'requirements')])
     else:
         logging.warn("Can't find pip: '%s' not found", pip)
 
@@ -119,7 +119,7 @@ COMMANDS = OrderedDict([
         provision_virtualenv,
     ]),
     ('[Local] Python Unit tests',
-        "./py3_venv/bin/nosetests -v tests/python/unittest/"
+        "pytest -v tests/python/unittest/"
     ),
     ('[Docker] Build the MXNet binary - outputs to "lib/"',
         "ci/build.py --platform ubuntu_cpu_lite /work/runtime_functions.sh build_ubuntu_cpu_docs"),
diff --git a/ci/docker/Dockerfile.build.centos7 b/ci/docker/Dockerfile.build.centos7
index ce74d9e..2a33584 100644
--- a/ci/docker/Dockerfile.build.centos7
+++ b/ci/docker/Dockerfile.build.centos7
@@ -51,7 +51,7 @@ RUN yum -y check-update || true && \
         protobuf-devel \
         # CentOS Software Collections https://www.softwarecollections.org
         devtoolset-7 \
-        rh-python35 \
+        rh-python36 \
         rh-maven35 \
         # Libraries
         # Provide clbas headerfiles
@@ -71,7 +71,7 @@ RUN yum -y check-update || true && \
 
 # Make GCC7, Python 3.5 and Maven 3.3 Software Collections available by default
 # during build and runtime of this container
-SHELL [ "/usr/bin/scl", "enable", "devtoolset-7", "rh-python35", "rh-maven35" ]
+SHELL [ "/usr/bin/scl", "enable", "devtoolset-7", "rh-python36", "rh-maven35" ]
 
 # Install minimum required cmake version
 RUN cd /usr/local/src && \
@@ -93,7 +93,16 @@ RUN cd /usr/local/src && \
 
 # Python dependencies
 RUN pip3 install --no-cache-dir --upgrade pip && \
-    pip3 install --no-cache-dir nose pylint cython numpy nose-timer requests h5py scipy==1.2.3 wheel
+    pip3 install --no-cache-dir pylint cython numpy requests h5py scipy==1.2.3 wheel \
+    pytest==5.3.5 \
+    pytest-env==0.6.2 \
+    pytest-cov==2.8.1 \
+    pytest-xdist==1.31.0 \
+    pytest-timeout==1.3.4 \
+    mock==2.0.0 \
+    onnx==1.5.0 \
+    protobuf==3.5.2 \
+    tabulate==0.7.5
 
 
 ARG USER_ID=0
diff --git a/ci/docker/Dockerfile.build.test.armv7 b/ci/docker/Dockerfile.build.test.armv7
index d49e7a5..066040c 100644
--- a/ci/docker/Dockerfile.build.test.armv7
+++ b/ci/docker/Dockerfile.build.test.armv7
@@ -27,15 +27,24 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
     python3-pip \
     python3-numpy \
     python3-scipy \
-    python3-nose \
-    python3-nose-timer \
     python3-requests \
  && rm -rf /var/lib/apt/lists/*
 
+
+# Python dependencies
+RUN pip3 install --no-cache-dir --upgrade pip && \
+    pip3 install --no-cache-dir \
+    pytest==5.3.5 \
+    pytest-env==0.6.2 \
+    pytest-cov==2.8.1 \
+    pytest-xdist==1.31.0 \
+    pytest-timeout==1.3.4 \
+    mock==2.0.0
+
 ARG USER_ID=0
 ARG GROUP_ID=0
 COPY install/ubuntu_adduser.sh /work/
 RUN /work/ubuntu_adduser.sh
 
 COPY runtime_functions.sh /work/
-WORKDIR /work/mxnet
\ No newline at end of file
+WORKDIR /work/mxnet
diff --git a/ci/docker/Dockerfile.build.test.armv8 b/ci/docker/Dockerfile.build.test.armv8
index bee4d85..7a77c78 100644
--- a/ci/docker/Dockerfile.build.test.armv8
+++ b/ci/docker/Dockerfile.build.test.armv8
@@ -27,15 +27,22 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
     python3-pip \
     python3-numpy \
     python3-scipy \
-    python3-nose \
-    python3-nose-timer \
     python3-requests \
  && rm -rf /var/lib/apt/lists/*
 
+RUN pip3 install --no-cache-dir --upgrade pip && \
+    pip3 install --no-cache-dir \
+    pytest==5.3.5 \
+    pytest-env==0.6.2 \
+    pytest-cov==2.8.1 \
+    pytest-xdist==1.31.0 \
+    pytest-timeout==1.3.4 \
+    mock==2.0.0
+
 ARG USER_ID=0
 ARG GROUP_ID=0
 COPY install/ubuntu_adduser.sh /work/
 RUN /work/ubuntu_adduser.sh
 
 COPY runtime_functions.sh /work/
-WORKDIR /work/mxnet
\ No newline at end of file
+WORKDIR /work/mxnet
diff --git a/ci/docker/Dockerfile.build.ubuntu_cpu b/ci/docker/Dockerfile.build.ubuntu_cpu
index 3c17b74..86536b2 100644
--- a/ci/docker/Dockerfile.build.ubuntu_cpu
+++ b/ci/docker/Dockerfile.build.ubuntu_cpu
@@ -54,12 +54,6 @@ RUN /work/ubuntu_gcc8.sh
 COPY install/ubuntu_mkl.sh /work/
 RUN /work/ubuntu_mkl.sh
 
-COPY install/ubuntu_caffe.sh /work/
-RUN /work/ubuntu_caffe.sh
-
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_r.sh /work/
 COPY install/r.gpg /work/
 RUN /work/ubuntu_r.sh
diff --git a/ci/docker/Dockerfile.build.ubuntu_cpu_julia b/ci/docker/Dockerfile.build.ubuntu_cpu_julia
index 3c17b74..86536b2 100644
--- a/ci/docker/Dockerfile.build.ubuntu_cpu_julia
+++ b/ci/docker/Dockerfile.build.ubuntu_cpu_julia
@@ -54,12 +54,6 @@ RUN /work/ubuntu_gcc8.sh
 COPY install/ubuntu_mkl.sh /work/
 RUN /work/ubuntu_mkl.sh
 
-COPY install/ubuntu_caffe.sh /work/
-RUN /work/ubuntu_caffe.sh
-
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_r.sh /work/
 COPY install/r.gpg /work/
 RUN /work/ubuntu_r.sh
diff --git a/ci/docker/Dockerfile.build.ubuntu_cpu_python b/ci/docker/Dockerfile.build.ubuntu_cpu_python
index 6b217d4..d1b0f65 100644
--- a/ci/docker/Dockerfile.build.ubuntu_cpu_python
+++ b/ci/docker/Dockerfile.build.ubuntu_cpu_python
@@ -32,9 +32,6 @@ COPY install/ubuntu_python.sh /work/
 COPY install/requirements /work/
 RUN /work/ubuntu_python.sh
 
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_docs.sh /work/
 RUN /work/ubuntu_docs.sh
 
@@ -46,4 +43,4 @@ RUN /work/ubuntu_adduser.sh
 
 COPY runtime_functions.sh /work/
 
-WORKDIR /work/mxnet
\ No newline at end of file
+WORKDIR /work/mxnet
diff --git a/ci/docker/Dockerfile.build.ubuntu_cpu_scala b/ci/docker/Dockerfile.build.ubuntu_cpu_scala
index d0ce477..a36e442 100644
--- a/ci/docker/Dockerfile.build.ubuntu_cpu_scala
+++ b/ci/docker/Dockerfile.build.ubuntu_cpu_scala
@@ -50,4 +50,4 @@ RUN /work/ubuntu_adduser.sh
 
 COPY runtime_functions.sh /work/
 
-WORKDIR /work/mxnet
\ No newline at end of file
+WORKDIR /work/mxnet
diff --git a/ci/docker/Dockerfile.build.ubuntu_gpu_cu101 b/ci/docker/Dockerfile.build.ubuntu_gpu_cu101
index aa2fdba..a5aa682 100644
--- a/ci/docker/Dockerfile.build.ubuntu_gpu_cu101
+++ b/ci/docker/Dockerfile.build.ubuntu_gpu_cu101
@@ -51,12 +51,6 @@ RUN /work/ubuntu_tvm.sh
 COPY install/ubuntu_llvm.sh /work/
 RUN /work/ubuntu_llvm.sh
 
-COPY install/ubuntu_caffe.sh /work/
-RUN /work/ubuntu_caffe.sh
-
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_docs.sh /work/
 COPY install/requirements /work/
 RUN /work/ubuntu_docs.sh
diff --git a/ci/docker/Dockerfile.build.ubuntu_nightly_cpu b/ci/docker/Dockerfile.build.ubuntu_nightly_cpu
index 47754b6..7cf8170 100644
--- a/ci/docker/Dockerfile.build.ubuntu_nightly_cpu
+++ b/ci/docker/Dockerfile.build.ubuntu_nightly_cpu
@@ -45,12 +45,6 @@ RUN /work/ubuntu_clang.sh
 COPY install/ubuntu_gcc8.sh /work/
 RUN /work/ubuntu_gcc8.sh
 
-COPY install/ubuntu_caffe.sh /work/
-RUN /work/ubuntu_caffe.sh
-
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_r.sh /work/
 COPY install/r.gpg /work/
 RUN /work/ubuntu_r.sh
diff --git a/ci/docker/Dockerfile.build.ubuntu_nightly_gpu b/ci/docker/Dockerfile.build.ubuntu_nightly_gpu
index e4e7bd1..424b9b3 100644
--- a/ci/docker/Dockerfile.build.ubuntu_nightly_gpu
+++ b/ci/docker/Dockerfile.build.ubuntu_nightly_gpu
@@ -51,12 +51,6 @@ RUN /work/ubuntu_tvm.sh
 COPY install/ubuntu_llvm.sh /work/
 RUN /work/ubuntu_llvm.sh
 
-COPY install/ubuntu_caffe.sh /work/
-RUN /work/ubuntu_caffe.sh
-
-COPY install/ubuntu_onnx.sh /work/
-RUN /work/ubuntu_onnx.sh
-
 COPY install/ubuntu_r.sh /work/
 COPY install/r.gpg /work/
 RUN /work/ubuntu_r.sh
diff --git a/ci/docker/install/requirements b/ci/docker/install/requirements
index 2d5125e..94f550b 100644
--- a/ci/docker/install/requirements
+++ b/ci/docker/install/requirements
@@ -18,17 +18,36 @@
 # build and install are separated so changes to build don't invalidate
 # the whole docker cache for the image
 
-boto3==1.9.229
-cpplint==1.3.0
+# Required dependencies
+numpy>=1.17
+requests>=2.20.0,<3
+graphviz<0.9.0,>=0.8.1
+
+# Optional dependencies
+onnx==1.5.0
+# protobuf version frozen due to ps-lite
+protobuf==3.5.2
+scipy==1.4.1
+tabulate==0.7.5
 Cython==0.29.7
-decorator==4.4.0
-h5py==2.8.0rc1
-mock==2.0.0
-nose==1.3.7
-nose-timer==0.7.3
-numpy>1.16.0,<2.0.0
+
+# Development dependencies
+cpplint==1.3.0
 pylint==2.3.1; python_version >= '3.0'
-requests<2.19.0,>=2.18.4
-scipy==1.2.1
-six==1.11.0
+pytest==5.3.5
+pytest-env==0.6.2
+pytest-cov==2.8.1
+pytest-xdist==1.31.0
+pytest-timeout==1.3.4
 setuptools
+mock==2.0.0
+
+# TVM dependencies
+decorator==4.4.0
+
+# Used in examples
+boto3==1.9.229
+h5py==2.10.0
+# TODO(szha): remove once clean-up for py2 is complete
+six==1.11.0
+Pillow<6
diff --git a/ci/docker/install/ubuntu_caffe.sh b/ci/docker/install/ubuntu_caffe.sh
deleted file mode 100755
index bda1c0b..0000000
--- a/ci/docker/install/ubuntu_caffe.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-set -ex
-
-apt-get update || true
-apt-get install -y \
-    libgflags-dev \
-    libgoogle-glog-dev \
-    libhdf5-serial-dev \
-    libleveldb-dev \
-    liblmdb-dev \
-    libopencv-dev \
-    libprotobuf-dev \
-    libsnappy-dev \
-    protobuf-compiler \
-    python-dev \
-    python-numpy \
-    python-opencv
-
-apt-get install -y --no-install-recommends libboost-all-dev
-
-cd /work/deps
-git clone http://github.com/BVLC/caffe.git
-
-cd caffe
-cp Makefile.config.example Makefile.config
-
-echo "CPU_ONLY := 1" >> Makefile.config
-
-# Fixes https://github.com/BVLC/caffe/issues/5658 See https://github.com/intel/caffe/wiki/Ubuntu-16.04-or-15.10-Installation-Guide
-echo "INCLUDE_DIRS += /usr/lib /usr/lib/x86_64-linux-gnu /usr/include/hdf5/serial/ " >> Makefile.config
-echo "LIBRARY_DIRS += /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/hdf5/serial " >> Makefile.config
-
-# Fixes https://github.com/BVLC/caffe/issues/4333 See https://github.com/intel/caffe/wiki/Ubuntu-16.04-or-15.10-Installation-Guide
-# Note: This is only valid on Ubuntu16.04 - the version numbers are bound to the distribution
-ln -s /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.10.0.2 /usr/lib/x86_64-linux-gnu/libhdf5.so
-ln -s /usr/lib/x86_64-linux-gnu/libhdf5_serial_hl.so.10.0.2 /usr/lib/x86_64-linux-gnu/libhdf5_hl.so
-
-make all pycaffe -j$(nproc)
-
-cd python
-for req in $(cat requirements.txt); do pip3 install $req; done
diff --git a/ci/docker/install/ubuntu_onnx.sh b/ci/docker/install/ubuntu_onnx.sh
deleted file mode 100755
index 44d6b9e..0000000
--- a/ci/docker/install/ubuntu_onnx.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-######################################################################
-# This script installs ONNX for Python along with all required dependencies 
-# on a Ubuntu Machine.
-# Tested on Ubuntu 16.04 distro.
-######################################################################
-
-set -e
-set -x
-
-echo "Installing libprotobuf-dev and protobuf-compiler ..."
-apt-get update || true
-apt-get install -y libprotobuf-dev protobuf-compiler
-
-echo "Installing pytest, pytest-cov, protobuf, Pillow, ONNX and tabulate ..."
-pip3 install pytest==3.6.3 pytest-cov==2.5.1 protobuf==3.5.2 onnx==1.3.0 Pillow==5.0.0 tabulate==0.7.5
diff --git a/ci/docker/install/ubuntu_python.sh b/ci/docker/install/ubuntu_python.sh
index b6792a2..6234aac 100755
--- a/ci/docker/install/ubuntu_python.sh
+++ b/ci/docker/install/ubuntu_python.sh
@@ -24,6 +24,7 @@ set -ex
 # install libraries for mxnet's python package on ubuntu
 apt-get update || true
 apt-get install -y python-dev python3-dev virtualenv wget
+apt-get install -y libprotobuf-dev protobuf-compiler
 
 # the version of the pip shipped with ubuntu may be too lower, install a recent version here
 wget -nv https://bootstrap.pypa.io/get-pip.py
diff --git a/ci/docker/runtime_functions.sh b/ci/docker/runtime_functions.sh
index b5ca888..458b1bc 100755
--- a/ci/docker/runtime_functions.sh
+++ b/ci/docker/runtime_functions.sh
@@ -22,8 +22,6 @@
 
 set -ex
 
-NOSE_COVERAGE_ARGUMENTS="--with-coverage --cover-inclusive --cover-xml --cover-branches --cover-package=mxnet"
-NOSE_TIMER_ARGUMENTS="--with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error"
 CI_CUDA_COMPUTE_CAPABILITIES="-gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_70,code=sm_70"
 CI_CMAKE_CUDA_ARCH="5.2 7.0"
 
@@ -974,16 +972,15 @@ sanity_check() {
     make cpplint jnilint
     make -f R-package/Makefile rcpplint
     make pylint
-    nosetests-3.4 tests/tutorials/test_sanity_tutorials.py
+    pytest tests/tutorials/test_sanity_tutorials.py
 }
 
 # Tests libmxnet
 # Parameters:
 # $1 -> mxnet_variant: The variant of the libmxnet.so library
-# $2 -> python_cmd: The python command to use to execute the tests, python or python3
 cd_unittest_ubuntu() {
     set -ex
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     export PYTHONPATH=./python/
     export MXNET_MKLDNN_DEBUG=0  # Ignored if not present
     export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0
@@ -994,10 +991,8 @@ cd_unittest_ubuntu() {
 
     local mxnet_variant=${1:?"This function requires a mxnet variant as the first argument"}
 
-    local nose_cmd="nosetests-3.4"
-
-    $nose_cmd $NOSE_TIMER_ARGUMENTS --verbose tests/python/unittest
-    $nose_cmd $NOSE_TIMER_ARGUMENTS --verbose tests/python/quantization
+    pytest --durations=50 --verbose tests/python/unittest
+    pytest --durations=50 --verbose tests/python/quantization
 
     # https://github.com/apache/incubator-mxnet/issues/11801
     # if [[ ${mxnet_variant} = "cpu" ]] || [[ ${mxnet_variant} = "mkl" ]]; then
@@ -1005,15 +1000,15 @@ cd_unittest_ubuntu() {
     # fi
 
     if [[ ${mxnet_variant} = cu* ]]; then
-        $nose_cmd $NOSE_TIMER_ARGUMENTS --verbose tests/python/gpu
+        pytest --durations=50 --verbose tests/python/gpu
 
         # Adding these here as CI doesn't test all CUDA environments
-        python3 example/image-classification/test_score.py
+        pytest example/image-classification/test_score.py
         integrationtest_ubuntu_gpu_dist_kvstore
     fi
 
-    if [[ ${mxnet_variant} = cpu ]]; then
-        $nose_cmd $NOSE_TIMER_ARGUMENTS --verbose tests/python/mkl
+    if [[ ${mxnet_variant} = *mkl ]]; then
+        pytest --durations=50 --verbose tests/python/mkl
     fi
 }
 
@@ -1025,8 +1020,8 @@ unittest_ubuntu_python3_cpu() {
     export MXNET_SUBGRAPH_VERBOSE=0
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_unittest.xml --verbose tests/python/unittest
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_quantization.xml --verbose tests/python/quantization
+    pytest --durations=50 --cov-report xml:tests_unittest.xml --verbose tests/python/unittest
+    pytest --durations=50 --cov-report xml:tests_quantization.xml --verbose tests/python/quantization
 }
 
 unittest_ubuntu_python3_cpu_mkldnn() {
@@ -1037,8 +1032,8 @@ unittest_ubuntu_python3_cpu_mkldnn() {
     export MXNET_SUBGRAPH_VERBOSE=0
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_unittest.xml --verbose tests/python/unittest
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_mkl.xml --verbose tests/python/mkl
+    pytest --durations=50 --cov-report xml:tests_unittest.xml --verbose tests/python/unittest
+    pytest --durations=50 --cov-report xml:tests_mkl.xml --verbose tests/python/mkl
 }
 
 unittest_ubuntu_python3_gpu() {
@@ -1050,7 +1045,7 @@ unittest_ubuntu_python3_gpu() {
     export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3}
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu
+    pytest --durations=50 --cov-report xml:tests_gpu.xml --verbose tests/python/gpu
 }
 
 unittest_ubuntu_python3_gpu_cython() {
@@ -1064,7 +1059,7 @@ unittest_ubuntu_python3_gpu_cython() {
     export MXNET_ENFORCE_CYTHON=1
     export DMLC_LOG_STACK_TRACE_DEPTH=10
     check_cython
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu
+    pytest --durations=50 --cov-report xml:tests_gpu.xml --verbose tests/python/gpu
 }
 
 unittest_ubuntu_python3_gpu_nocudnn() {
@@ -1075,7 +1070,7 @@ unittest_ubuntu_python3_gpu_nocudnn() {
     export CUDNN_OFF_TEST_ONLY=true
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu
+    pytest --durations=50 --cov-report xml:tests_gpu.xml --verbose tests/python/gpu
 }
 
 unittest_ubuntu_tensorrt_gpu() {
@@ -1087,8 +1082,8 @@ unittest_ubuntu_tensorrt_gpu() {
     export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3}
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    tests/python/tensorrt/lenet5_train.py
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_trt_gpu.xml --verbose --nocapture tests/python/tensorrt/
+    python3 tests/python/tensorrt/lenet5_train.py
+    pytest --durations=50 --cov-report xml:tests_trt_gpu.xml --verbose --capture=no tests/python/tensorrt/
 }
 
 # quantization gpu currently only runs on P3 instances
@@ -1096,7 +1091,7 @@ unittest_ubuntu_tensorrt_gpu() {
 unittest_ubuntu_python3_quantization_gpu() {
     set -ex
     if [ -f /etc/redhat-release ]; then
-        source /opt/rh/rh-python35/enable
+        source /opt/rh/rh-python36/enable
     fi
     export PYTHONPATH=./python/
     export MXNET_MKLDNN_DEBUG=0 # Ignored if not present
@@ -1105,7 +1100,7 @@ unittest_ubuntu_python3_quantization_gpu() {
     export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3}
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_quantization_gpu.xml --verbose tests/python/quantization_gpu
+    pytest --durations=50 --cov-report xml:tests_quantization_gpu.xml --verbose tests/python/quantization_gpu
 }
 
 unittest_centos7_cpu_scala() {
@@ -1245,29 +1240,29 @@ unittest_ubuntu_cpu_julia10() {
 
 unittest_centos7_cpu() {
     set -ex
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     cd /work/mxnet
-    python -m "nose" $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_unittest.xml --verbose tests/python/unittest
-    python -m "nose" $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_train.xml --verbose tests/python/train
+    python -m pytest --durations=50 --cov-report xml:tests_unittest.xml --verbose tests/python/unittest
+    python -m pytest --durations=50 --cov-report xml:tests_train.xml --verbose tests/python/train
 }
 
 unittest_centos7_gpu() {
     set -ex
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     cd /work/mxnet
     export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3}
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    python3 -m "nose" $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu
+    python3 -m pytest --durations=50 --cov-report xml:tests_gpu.xml --verbose tests/python/gpu
 }
 
 integrationtest_ubuntu_cpu_onnx() {
 	set -ex
 	export PYTHONPATH=./python/
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-	tests/python-pytest/onnx/backend_test.py
-	pytest tests/python-pytest/onnx/mxnet_export_test.py
-	pytest tests/python-pytest/onnx/test_models.py
-	pytest tests/python-pytest/onnx/test_node.py
+	python3 tests/python/unittest/onnx/backend_test.py
+	pytest tests/python/unittest/onnx/mxnet_export_test.py
+	pytest tests/python/unittest/onnx/test_models.py
+	pytest tests/python/unittest/onnx/test_node.py
 }
 
 integrationtest_ubuntu_gpu_python() {
@@ -1276,14 +1271,7 @@ integrationtest_ubuntu_gpu_python() {
     export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0
     export MXNET_SUBGRAPH_VERBOSE=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    example/image-classification/test_score.py
-}
-
-integrationtest_ubuntu_gpu_caffe() {
-    set -ex
-    export PYTHONPATH=/work/deps/caffe/python:./python
-    export DMLC_LOG_STACK_TRACE_DEPTH=10
-    tools/caffe_converter/test_converter.py
+    pytest example/image-classification/test_score.py
 }
 
 integrationtest_ubuntu_cpu_asan() {
@@ -1379,10 +1367,9 @@ test_ubuntu_cpu_python3() {
     source $VENV/bin/activate
 
     cd /work/mxnet/python
-    pip3 install nose nose-timer
     pip3 install -e .
     cd /work/mxnet
-    python3 -m "nose" $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --verbose tests/python/unittest
+    python3 -m pytest $TEST_TIMER_ARGUMENTS --verbose tests/python/unittest
 
     popd
 }
@@ -1396,7 +1383,7 @@ unittest_ubuntu_python3_arm() {
     export MXNET_SUBGRAPH_VERBOSE=0
     export MXNET_ENABLE_CYTHON=0
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    python3 -m nose --verbose tests/python/unittest/test_engine.py
+    python3 -m pytest --verbose tests/python/unittest/test_engine.py
 }
 
 # Functions that run the nightly Tests:
@@ -1470,9 +1457,9 @@ nightly_test_large_tensor() {
     set -ex
     export PYTHONPATH=./python/
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 tests/nightly/test_large_array.py:test_tensor
-    nosetests-3.4 tests/nightly/test_large_array.py:test_nn
-    nosetests-3.4 tests/nightly/test_large_array.py:test_basic
+    pytest tests/nightly/test_large_array.py::test_tensor
+    pytest tests/nightly/test_large_array.py::test_nn
+    pytest tests/nightly/test_large_array.py::test_basic
 }
 
 #Test Large Vectors
@@ -1480,9 +1467,9 @@ nightly_test_large_vector() {
     set -ex
     export PYTHONPATH=./python/
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_tensor
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_nn
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_basic
+    pytest tests/nightly/test_large_vector.py::test_tensor
+    pytest tests/nightly/test_large_vector.py::test_nn
+    pytest tests/nightly/test_large_vector.py::test_basic
 }
 
 #Test Large Vectors
@@ -1490,9 +1477,9 @@ nightly_test_large_vector() {
     set -ex
     export PYTHONPATH=./python/
     export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_tensor
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_nn
-    nosetests-3.4 tests/nightly/test_large_vector.py:test_basic
+    pytest tests/nightly/test_large_vector.py::test_tensor
+    pytest tests/nightly/test_large_vector.py::test_nn
+    pytest tests/nightly/test_large_vector.py::test_basic
 }
 
 #Tests Amalgamation Build with 5 different sets of flags
@@ -1536,26 +1523,6 @@ nightly_model_backwards_compat_train() {
     ./tests/nightly/model_backwards_compatibility_check/train_mxnet_legacy_models.sh
 }
 
-nightly_straight_dope_python3_single_gpu_tests() {
-    set -ex
-    cd /work/mxnet/tests/nightly/straight_dope
-    export PYTHONPATH=/work/mxnet/python/
-    export MXNET_TEST_KERNEL=python3
-    export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_straight_dope_python3_single_gpu.xml \
-      test_notebooks_single_gpu.py --nologcapture
-}
-
-nightly_straight_dope_python3_multi_gpu_tests() {
-    set -ex
-    cd /work/mxnet/tests/nightly/straight_dope
-    export PYTHONPATH=/work/mxnet/python/
-    export MXNET_TEST_KERNEL=python3
-    export DMLC_LOG_STACK_TRACE_DEPTH=10
-    nosetests-3.4 $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_straight_dope_python3_multi_gpu.xml \
-      test_notebooks_multi_gpu.py --nologcapture
-}
-
 nightly_tutorial_test_ubuntu_python3_gpu() {
     set -ex
     cd /work/mxnet/docs
@@ -1568,7 +1535,7 @@ nightly_tutorial_test_ubuntu_python3_gpu() {
     export PYTHONPATH=/work/mxnet/python/
     export MXNET_TUTORIAL_TEST_KERNEL=python3
     cd /work/mxnet/tests/tutorials
-    nosetests-3.4 $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_tutorials.xml test_tutorials.py --nologcapture
+    pytest --durations=50 --cov-report xml:tests_tutorials.xml --capture=no test_tutorials.py
 }
 
 nightly_java_demo_test_cpu() {
@@ -1592,8 +1559,8 @@ nightly_estimator() {
     export DMLC_LOG_STACK_TRACE_DEPTH=10
     cd /work/mxnet/tests/nightly/estimator
     export PYTHONPATH=/work/mxnet/python/
-    nosetests test_estimator_cnn.py
-    nosetests test_sentiment_rnn.py
+    pytest test_estimator_cnn.py
+    pytest test_sentiment_rnn.py
 }
 
 # For testing PRs
@@ -1947,7 +1914,7 @@ build_static_libmxnet() {
     set -ex
     pushd .
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     export USE_SYSTEM_CUDA=1
     local mxnet_variant=${1:?"This function requires a python command as the first argument"}
     source tools/staticbuild/build.sh ${mxnet_variant}
@@ -1959,7 +1926,7 @@ cd_package_pypi() {
     set -ex
     pushd .
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     local mxnet_variant=${1:?"This function requires a python command as the first argument"}
     ./cd/python/pypi/pypi_package.sh ${mxnet_variant}
     popd
@@ -1968,33 +1935,31 @@ cd_package_pypi() {
 # Sanity checks wheel file
 cd_integration_test_pypi() {
     set -ex
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
 
     local gpu_enabled=${1:-"false"}
 
     local test_conv_params=''
     local mnist_params=''
 
-    local pip_cmd='pip3'
-
     if [ "${gpu_enabled}" = "true" ]; then
         mnist_params="--gpu 0"
         test_conv_params="--gpu"
     fi
 
     # install mxnet wheel package
-    ${pip_cmd} install --user ./wheel_build/dist/*.whl
+    pip3 install --user ./wheel_build/dist/*.whl
 
     # execute tests
-    ${python_cmd} /work/mxnet/tests/python/train/test_conv.py ${test_conv_params}
-    ${python_cmd} /work/mxnet/example/image-classification/train_mnist.py ${mnist_params}
+    python3 /work/mxnet/tests/python/train/test_conv.py ${test_conv_params}
+    python3 /work/mxnet/example/image-classification/train_mnist.py ${mnist_params}
 }
 
 # Publishes wheel to PyPI
 cd_pypi_publish() {
     set -ex
     pip3 install --user twine
-    ./cd/python/pypi/pypi_publish.py `readlink -f wheel_build/dist/*.whl`
+    python3 ./cd/python/pypi/pypi_publish.py `readlink -f wheel_build/dist/*.whl`
 }
 
 cd_s3_publish() {
@@ -2026,7 +1991,7 @@ build_static_python_cpu() {
     pushd .
     export mxnet_variant=cpu
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     ./ci/publish/python/build.sh
     popd
 }
@@ -2037,7 +2002,7 @@ build_static_python_cu92() {
     export mxnet_variant=cu92
     export USE_SYSTEM_CUDA=1
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     ./ci/publish/python/build.sh
     popd
 }
@@ -2048,7 +2013,7 @@ build_static_python_cpu_cmake() {
     export mxnet_variant=cpu
     export CMAKE_STATICBUILD=1
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     ./ci/publish/python/build.sh
     popd
 }
@@ -2060,7 +2025,7 @@ build_static_python_cu92_cmake() {
     export CMAKE_STATICBUILD=1
     export USE_SYSTEM_CUDA=1
     source /opt/rh/devtoolset-7/enable
-    source /opt/rh/rh-python35/enable
+    source /opt/rh/rh-python36/enable
     ./ci/publish/python/build.sh
     popd
 }
diff --git a/ci/docker_cache.py b/ci/docker_cache.py
old mode 100755
new mode 100644
diff --git a/ci/docker_login.py b/ci/docker_login.py
old mode 100755
new mode 100644
diff --git a/ci/jenkins/Jenkins_steps.groovy b/ci/jenkins/Jenkins_steps.groovy
index bfa5173..f693649 100644
--- a/ci/jenkins/Jenkins_steps.groovy
+++ b/ci/jenkins/Jenkins_steps.groovy
@@ -807,8 +807,8 @@ def test_unix_python3_cpu() {
             python3_ut('ubuntu_cpu')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_cpu_unittest.xml')
-            utils.collect_test_results_unix('nosetests_quantization.xml', 'nosetests_python3_cpu_quantization.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_cpu_unittest.xml')
+            utils.collect_test_results_unix('tests_quantization.xml', 'tests_python3_cpu_quantization.xml')
           }
         }
       }
@@ -824,8 +824,8 @@ def test_unix_python3_mkl_cpu() {
             python3_ut('ubuntu_cpu')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_cpu_unittest.xml')
-            utils.collect_test_results_unix('nosetests_quantization.xml', 'nosetests_python3_cpu_quantization.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_cpu_unittest.xml')
+            utils.collect_test_results_unix('tests_quantization.xml', 'tests_python3_cpu_quantization.xml')
           }
         }
       }
@@ -841,7 +841,7 @@ def test_unix_python3_gpu() {
             python3_gpu_ut_cython('ubuntu_gpu_cu101')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_gpu.xml', 'nosetests_python3_gpu.xml')
+            utils.collect_test_results_unix('tests_gpu.xml', 'tests_python3_gpu.xml')
           }
         }
       }
@@ -857,7 +857,7 @@ def test_unix_python3_gpu_no_tvm_op() {
             python3_gpu_ut_cython('ubuntu_gpu_cu101')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_gpu.xml', 'nosetests_python3_gpu.xml')
+            utils.collect_test_results_unix('tests_gpu.xml', 'tests_python3_gpu.xml')
           }
         }
       }
@@ -874,7 +874,7 @@ def test_unix_python3_quantize_gpu() {
               utils.docker_run('ubuntu_gpu_cu101', 'unittest_ubuntu_python3_quantization_gpu', true)
               utils.publish_test_coverage()
             } finally {
-              utils.collect_test_results_unix('nosetests_quantization_gpu.xml', 'nosetests_python3_quantize_gpu.xml')
+              utils.collect_test_results_unix('tests_quantization_gpu.xml', 'tests_python3_quantize_gpu.xml')
             }
           }
         }
@@ -890,8 +890,8 @@ def test_unix_python3_debug_cpu() {
             utils.unpack_and_init('cpu_debug', mx_cmake_lib_debug, true)
             python3_ut('ubuntu_cpu')
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_cpu_debug_unittest.xml')
-            utils.collect_test_results_unix('nosetests_quantization.xml', 'nosetests_python3_cpu_debug_quantization.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_cpu_debug_unittest.xml')
+            utils.collect_test_results_unix('tests_quantization.xml', 'tests_python3_cpu_debug_quantization.xml')
           }
         }
       }
@@ -906,8 +906,8 @@ def test_unix_python3_cpu_no_tvm_op() {
             utils.unpack_and_init('cpu_openblas_no_tvm_op', mx_cmake_lib_no_tvm_op)
             python3_ut('ubuntu_cpu')
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_cpu_no_tvm_op_unittest.xml')
-            utils.collect_test_results_unix('nosetests_quantization.xml', 'nosetests_python3_cpu_no_tvm_op_quantization.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_cpu_no_tvm_op_unittest.xml')
+            utils.collect_test_results_unix('tests_quantization.xml', 'tests_python3_cpu_no_tvm_op_quantization.xml')
           }
         }
       }
@@ -923,8 +923,8 @@ def test_unix_python3_mkldnn_cpu() {
             python3_ut_mkldnn('ubuntu_cpu')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_mkldnn_cpu_unittest.xml')
-            utils.collect_test_results_unix('nosetests_mkl.xml', 'nosetests_python3_mkldnn_cpu_mkl.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_mkldnn_cpu_unittest.xml')
+            utils.collect_test_results_unix('tests_mkl.xml', 'tests_python3_mkldnn_cpu_mkl.xml')
           }
         }
       }
@@ -940,8 +940,8 @@ def test_unix_python3_mkldnn_mkl_cpu() {
             python3_ut_mkldnn('ubuntu_cpu')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_mkldnn_cpu_unittest.xml')
-            utils.collect_test_results_unix('nosetests_mkl.xml', 'nosetests_python3_mkldnn_cpu_mkl.xml')
+            utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_mkldnn_cpu_unittest.xml')
+            utils.collect_test_results_unix('tests_mkl.xml', 'tests_python3_mkldnn_cpu_mkl.xml')
           }
         }
       }
@@ -957,7 +957,7 @@ def test_unix_python3_mkldnn_gpu() {
             python3_gpu_ut('ubuntu_gpu_cu101')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_gpu.xml', 'nosetests_python3_mkldnn_gpu.xml')
+            utils.collect_test_results_unix('tests_gpu.xml', 'tests_python3_mkldnn_gpu.xml')
           }
         }
       }
@@ -973,7 +973,7 @@ def test_unix_python3_mkldnn_nocudnn_gpu() {
             python3_gpu_ut_nocudnn('ubuntu_gpu_cu101')
             utils.publish_test_coverage()
           } finally {
-            utils.collect_test_results_unix('nosetests_gpu.xml', 'nosetests_python3_mkldnn_gpu_nocudnn.xml')
+            utils.collect_test_results_unix('tests_gpu.xml', 'tests_python3_mkldnn_gpu_nocudnn.xml')
           }
         }
       }
@@ -990,7 +990,7 @@ def test_unix_python3_tensorrt_gpu() {
               utils.docker_run('ubuntu_gpu_tensorrt', 'unittest_ubuntu_tensorrt_gpu', true)
               utils.publish_test_coverage()
             } finally {
-              utils.collect_test_results_unix('nosetests_tensorrt.xml', 'nosetests_python3_tensorrt_gpu.xml')
+              utils.collect_test_results_unix('tests_tensorrt.xml', 'tests_python3_tensorrt_gpu.xml')
             }
           }
         }
@@ -1012,21 +1012,6 @@ def test_unix_python3_integration_gpu() {
     }]
 }
 
-def test_unix_caffe_gpu() {
-    return ['Caffe GPU': {
-        node(NODE_LINUX_GPU) {
-            ws('workspace/it-caffe') {
-            timeout(time: max_time, unit: 'MINUTES') {
-                utils.init_git()
-                utils.unpack_lib('gpu', mx_lib)
-                utils.docker_run('ubuntu_gpu_cu101', 'integrationtest_ubuntu_gpu_caffe', true)
-                utils.publish_test_coverage()
-            }
-            }
-        }
-    }]
-}
-
 def test_unix_cpp_package_gpu() {
     return ['cpp-package GPU Makefile': {
       node(NODE_LINUX_GPU) {
@@ -1300,8 +1285,8 @@ def test_centos7_python3_cpu() {
               utils.docker_run('centos7_cpu', 'unittest_centos7_cpu', false)
               utils.publish_test_coverage()
             } finally {
-              utils.collect_test_results_unix('nosetests_unittest.xml', 'nosetests_python3_centos7_cpu_unittest.xml')
-              utils.collect_test_results_unix('nosetests_train.xml', 'nosetests_python3_centos7_cpu_train.xml')
+              utils.collect_test_results_unix('tests_unittest.xml', 'tests_python3_centos7_cpu_unittest.xml')
+              utils.collect_test_results_unix('tests_train.xml', 'tests_python3_centos7_cpu_train.xml')
             }
           }
         }
@@ -1319,7 +1304,7 @@ def test_centos7_python3_gpu() {
               utils.docker_run('centos7_gpu_cu92', 'unittest_centos7_gpu', true)
               utils.publish_test_coverage()
             } finally {
-              utils.collect_test_results_unix('nosetests_gpu.xml', 'nosetests_python3_centos7_gpu.xml')
+              utils.collect_test_results_unix('tests_gpu.xml', 'tests_python3_centos7_gpu.xml')
             }
           }
         }
@@ -1351,8 +1336,8 @@ def test_windows_python3_gpu() {
               unstash 'windows_package_gpu'
               powershell 'ci/windows/test_py3_gpu.ps1'
             } finally {
-              utils.collect_test_results_windows('nosetests_forward.xml', 'nosetests_gpu_forward_windows_python3_gpu.xml')
-              utils.collect_test_results_windows('nosetests_operator.xml', 'nosetests_gpu_operator_windows_python3_gpu.xml')
+              utils.collect_test_results_windows('tests_forward.xml', 'tests_gpu_forward_windows_python3_gpu.xml')
+              utils.collect_test_results_windows('tests_operator.xml', 'tests_gpu_operator_windows_python3_gpu.xml')
             }
           }
         }
@@ -1370,8 +1355,8 @@ def test_windows_python3_gpu_mkldnn() {
               unstash 'windows_package_gpu_mkldnn'
               powershell 'ci/windows/test_py3_gpu.ps1'
             } finally {
-              utils.collect_test_results_windows('nosetests_forward.xml', 'nosetests_gpu_forward_windows_python3_gpu_mkldnn.xml')
-              utils.collect_test_results_windows('nosetests_operator.xml', 'nosetests_gpu_operator_windows_python3_gpu_mkldnn.xml')
+              utils.collect_test_results_windows('tests_forward.xml', 'tests_gpu_forward_windows_python3_gpu_mkldnn.xml')
+              utils.collect_test_results_windows('tests_operator.xml', 'tests_gpu_operator_windows_python3_gpu_mkldnn.xml')
             }
           }
         }
@@ -1389,7 +1374,7 @@ def test_windows_python3_cpu() {
               unstash 'windows_package_cpu'
               powershell 'ci/windows/test_py3_cpu.ps1'
             } finally {
-              utils.collect_test_results_windows('nosetests_unittest.xml', 'nosetests_unittest_windows_python3_cpu.xml')
+              utils.collect_test_results_windows('tests_unittest.xml', 'tests_unittest_windows_python3_cpu.xml')
             }
           }
         }
diff --git a/ci/jenkins/Jenkinsfile_centos_cpu b/ci/jenkins/Jenkinsfile_centos_cpu
index e05d326..374be1c 100644
--- a/ci/jenkins/Jenkinsfile_centos_cpu
+++ b/ci/jenkins/Jenkinsfile_centos_cpu
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_centos_gpu b/ci/jenkins/Jenkinsfile_centos_gpu
index 31fad5c..3c6c28d 100644
--- a/ci/jenkins/Jenkinsfile_centos_gpu
+++ b/ci/jenkins/Jenkinsfile_centos_gpu
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_clang b/ci/jenkins/Jenkinsfile_clang
index 1365b31..953d4b4 100644
--- a/ci/jenkins/Jenkinsfile_clang
+++ b/ci/jenkins/Jenkinsfile_clang
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_edge b/ci/jenkins/Jenkinsfile_edge
index 9e2abf5..857d081 100644
--- a/ci/jenkins/Jenkinsfile_edge
+++ b/ci/jenkins/Jenkinsfile_edge
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_miscellaneous b/ci/jenkins/Jenkinsfile_miscellaneous
index 68d0de4..a47e4c8 100644
--- a/ci/jenkins/Jenkinsfile_miscellaneous
+++ b/ci/jenkins/Jenkinsfile_miscellaneous
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 
 node('utility') {
diff --git a/ci/jenkins/Jenkinsfile_sanity b/ci/jenkins/Jenkinsfile_sanity
index ed4d16e..48f5574 100644
--- a/ci/jenkins/Jenkinsfile_sanity
+++ b/ci/jenkins/Jenkinsfile_sanity
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_tools b/ci/jenkins/Jenkinsfile_tools
index 9af63aa..f9e17ed 100644
--- a/ci/jenkins/Jenkinsfile_tools
+++ b/ci/jenkins/Jenkinsfile_tools
@@ -23,7 +23,7 @@
 // A place to add tests scripts for supporting tools
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_unix_gpu b/ci/jenkins/Jenkinsfile_unix_gpu
index 34ee5af..7742a65 100644
--- a/ci/jenkins/Jenkinsfile_unix_gpu
+++ b/ci/jenkins/Jenkinsfile_unix_gpu
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
@@ -59,12 +59,10 @@ core_logic: {
     custom_steps.test_unix_python3_integration_gpu(),
     custom_steps.test_unix_cpp_package_gpu(),
     custom_steps.test_unix_scala_gpu(),
-    custom_steps.test_unix_distributed_kvstore_gpu(),
+    // TODO(szha): fix and reenable the hanging issue. tracked in #18098
+    // custom_steps.test_unix_distributed_kvstore_gpu(),
     custom_steps.test_unix_python3_gpu_no_tvm_op(),
     custom_steps.test_unix_capi_cpp_package(),
-
-    // Disabled due to: https://github.com/apache/incubator-mxnet/issues/11407
-    //custom_steps.test_unix_caffe_gpu()
   ]) 
 }
 ,
diff --git a/ci/jenkins/Jenkinsfile_website_beta b/ci/jenkins/Jenkinsfile_website_beta
index 9220052..55c8ab8 100644
--- a/ci/jenkins/Jenkinsfile_website_beta
+++ b/ci/jenkins/Jenkinsfile_website_beta
@@ -22,7 +22,7 @@
 // This pipeline will publish to https://mxnet-beta.staged.apache.org/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('restricted-utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_website_full b/ci/jenkins/Jenkinsfile_website_full
index 39cc6f4..ecf1f29 100644
--- a/ci/jenkins/Jenkinsfile_website_full
+++ b/ci/jenkins/Jenkinsfile_website_full
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('restricted-utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_website_full_pr b/ci/jenkins/Jenkinsfile_website_full_pr
index 133c6c2..9823fb3 100644
--- a/ci/jenkins/Jenkinsfile_website_full_pr
+++ b/ci/jenkins/Jenkinsfile_website_full_pr
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_website_mxnet_build b/ci/jenkins/Jenkinsfile_website_mxnet_build
index a3c3330..2acb159 100644
--- a/ci/jenkins/Jenkinsfile_website_mxnet_build
+++ b/ci/jenkins/Jenkinsfile_website_mxnet_build
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_website_nightly b/ci/jenkins/Jenkinsfile_website_nightly
index 16c333e..1bbe5bc 100644
--- a/ci/jenkins/Jenkinsfile_website_nightly
+++ b/ci/jenkins/Jenkinsfile_website_nightly
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('restricted-utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_windows_cpu b/ci/jenkins/Jenkinsfile_windows_cpu
index 3de8f7d..b699664 100644
--- a/ci/jenkins/Jenkinsfile_windows_cpu
+++ b/ci/jenkins/Jenkinsfile_windows_cpu
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/jenkins/Jenkinsfile_windows_gpu b/ci/jenkins/Jenkinsfile_windows_gpu
index 631d9e9..b5db665 100644
--- a/ci/jenkins/Jenkinsfile_windows_gpu
+++ b/ci/jenkins/Jenkinsfile_windows_gpu
@@ -21,7 +21,7 @@
 // See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/
 
 // timeout in minutes
-max_time = 180
+max_time = 240
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/ci/safe_docker_run.py b/ci/safe_docker_run.py
old mode 100755
new mode 100644
diff --git a/ci/test_docker_cache.py b/ci/test_docker_cache.py
index aeb399f..81b315b 100644
--- a/ci/test_docker_cache.py
+++ b/ci/test_docker_cache.py
@@ -270,8 +270,3 @@ def _assert_docker_build(lambda_func, expected_cache_hit_count: int, expected_ca
         assert output.count('Using cache') == expected_cache_hit_count, \
             'Expected {} "Using cache", got {}. Log:{}'.\
                 format(expected_cache_hit_count, output.count('Using cache'), output)
-
-
-if __name__ == '__main__':
-    import nose
-    nose.main()
diff --git a/ci/test_docker_login.py b/ci/test_docker_login.py
index 6c989ad..488295d 100644
--- a/ci/test_docker_login.py
+++ b/ci/test_docker_login.py
@@ -228,7 +228,3 @@ class TestDockerLogin(unittest.TestCase):
                 with self.assertRaises(RuntimeError):
                     main(["--secret-name", "name"])
 
-
-if __name__ == '__main__':
-    import nose
-    nose.main()
diff --git a/ci/test_safe_docker_run.py b/ci/test_safe_docker_run.py
index 433d42e..5b75c0e 100644
--- a/ci/test_safe_docker_run.py
+++ b/ci/test_safe_docker_run.py
@@ -420,8 +420,3 @@ class TestSafeDockerRun(unittest.TestCase):
 
             # The container should no longer exist
             assert get_container(container_name) is None
-
-
-if __name__ == '__main__':
-    import nose
-    nose.main()
diff --git a/ci/windows/test_py3_cpu.ps1 b/ci/windows/test_py3_cpu.ps1
index 8f520bb..84f1da8 100644
--- a/ci/windows/test_py3_cpu.ps1
+++ b/ci/windows/test_py3_cpu.ps1
@@ -23,12 +23,12 @@ $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0
 $env:MXNET_SUBGRAPH_VERBOSE=0
 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home')
 
-C:\Python37\Scripts\pip install -r tests\requirements.txt
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest
+C:\Python37\Scripts\pip install -r ci\docker\install\requirements
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_unittest.xml tests\python\unittest
 if ($LastExitCode -ne 0) { Throw ("Error running unittest, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_train.xml tests\python\train
 if ($LastExitCode -ne 0) { Throw ("Error running train tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
 # Adding this extra test since it's not possible to set env var on the fly in Windows.
 $env:MXNET_SAFE_ACCUMULATION=1
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest\test_operator.py:test_norm
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_unittest.xml --cov-append tests\python\unittest\test_operator.py::test_norm
 if ($LastExitCode -ne 0) { Throw ("Error running unittest, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
diff --git a/ci/windows/test_py3_gpu.ps1 b/ci/windows/test_py3_gpu.ps1
index 0ce3d95..e83b865 100644
--- a/ci/windows/test_py3_gpu.ps1
+++ b/ci/windows/test_py3_gpu.ps1
@@ -23,16 +23,18 @@ $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0
 $env:MXNET_SUBGRAPH_VERBOSE=0
 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home')
 
-C:\Python37\Scripts\pip install -r tests\requirements.txt
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest
+C:\Python37\Scripts\pip install -r ci\docker\install\requirements
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_unittest.xml tests\python\unittest
 if ($LastExitCode -ne 0) { Throw ("Error running unittest, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_operator.xml tests\python\gpu\test_operator_gpu.py
 if ($LastExitCode -ne 0) { Throw ("Error running tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_forward.xml tests\python\gpu\test_forward.py
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_forward.xml tests\python\gpu\test_forward.py
 if ($LastExitCode -ne 0) { Throw ("Error running tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_train.xml tests\python\train
 if ($LastExitCode -ne 0) { Throw ("Error running tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
 # Adding this extra test since it's not possible to set env var on the fly in Windows.
 $env:MXNET_SAFE_ACCUMULATION=1
-C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py:test_norm
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_operator.xml --cov-append tests\python\gpu\test_operator_gpu.py::test_norm
 if ($LastExitCode -ne 0) { Throw ("Error running tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
+C:\Python37\python.exe -m pytest -v --durations=50 --cov-report xml:tests_tvm_op.xml tests\python\gpu\test_tvm_op_gpu.py
+if ($LastExitCode -ne 0) { Throw ("Error running TVM op tests, python exited with status code " + ('{0:X}' -f $LastExitCode)) }
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..dad6854
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,226 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""conftest.py contains configuration for pytest.
+
+Configuration file for tests in tests/ and scripts/ folders.
+
+Note that fixtures of higher-scoped fixtures (such as ``session``) are
+instantiated before lower-scoped fixtures (such as ``function``).
+
+"""
+
+import logging
+import os
+import random
+
+import pytest
+
+
+def pytest_sessionfinish(session, exitstatus):
+    if exitstatus == 5:  # Don't fail if no tests were run
+        session.exitstatus = 0
+
+
+# * Random seed setup
+def pytest_configure():
+    """Pytest configuration hook to help reproduce test segfaults
+
+    Sets and outputs rng seeds.
+
+    The segfault-debug procedure on a module called test_module.py is:
+
+    1. run "pytest --verbose test_module.py".  A seg-faulting output might be:
+
+       [INFO] np, mx and python random seeds = 4018804151
+       test_module.test1 ... ok
+       test_module.test2 ... Illegal instruction (core dumped)
+
+    2. Copy the module-starting seed into the next command, then run:
+
+       MXNET_MODULE_SEED=4018804151 pytest --log-level=DEBUG --verbose test_module.py
+
+       Output might be:
+
+       [WARNING] **** module-level seed is set: all tests running deterministically ****
+       [INFO] np, mx and python random seeds = 4018804151
+       test_module.test1 ... [DEBUG] np and mx random seeds = 3935862516
+       ok
+       test_module.test2 ... [DEBUG] np and mx random seeds = 1435005594
+       Illegal instruction (core dumped)
+
+    3. Copy the segfaulting-test seed into the command:
+       MXNET_TEST_SEED=1435005594 pytest --log-level=DEBUG --verbose test_module.py:test2
+       Output might be:
+
+       [INFO] np, mx and python random seeds = 2481884723
+       test_module.test2 ... [DEBUG] np and mx random seeds = 1435005594
+       Illegal instruction (core dumped)
+
+    3. Finally reproduce the segfault directly under gdb (might need additional os packages)
+       by editing the bottom of test_module.py to be
+
+       if __name__ == '__main__':
+           logging.getLogger().setLevel(logging.DEBUG)
+           test2()
+
+       MXNET_TEST_SEED=1435005594 gdb -ex r --args python test_module.py
+
+    4. When finished debugging the segfault, remember to unset any exported MXNET_ seed
+       variables in the environment to return to non-deterministic testing (a good thing).
+    """
+
+    module_seed_str = os.getenv('MXNET_MODULE_SEED')
+    if module_seed_str is None:
+        seed = random.randint(0, 2**31-1)
+    else:
+        seed = int(module_seed_str)
+        logging.warning('*** module-level seed is set: '
+                        'all tests running deterministically ***')
+    logging.info('Setting module np/mx/python random seeds, '
+                 'use MXNET_MODULE_SEED={} to reproduce.'.format(seed))
+
+    random.seed(seed)
+    try:
+        import numpy as np
+        import mxnet as mx
+        np.random.seed(seed)
+        mx.random.seed(seed)
+    except:
+        logging.warning('Unable to import numpy/mxnet. Skipping conftest.')
+
+    # The MXNET_TEST_SEED environment variable will override MXNET_MODULE_SEED for tests with
+    #  the 'with_seed()' decoration.  Inform the user of this once here at the module level.
+    if os.getenv('MXNET_TEST_SEED') is not None:
+        logging.warning('*** test-level seed set: all "@with_seed()" '
+                        'tests run deterministically ***')
+
+
+@pytest.hookimpl(tryfirst=True, hookwrapper=True)
+def pytest_runtest_makereport(item, call):
+    """Make test outcome available to fixture.
+
+    https://docs.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures
+    """
+    # execute all other hooks to obtain the report object
+    outcome = yield
+    rep = outcome.get_result()
+
+    # set a report attribute for each phase of a call, which can
+    # be "setup", "call", "teardown"
+    setattr(item, "rep_" + rep.when, rep)
+
+
+@pytest.fixture(scope='function', autouse=True)
+def function_scope_seed(request):
+    """A function scope fixture that manages rng seeds.
+
+    This fixture automatically initializes the python, numpy and mxnet random
+    number generators randomly on every test run.
+
+    def test_ok_with_random_data():
+        ...
+
+    To fix the seed used for a test case mark the test function with the
+    desired seed:
+
+    @pytest.mark.seed(1)
+    def test_not_ok_with_random_data():
+        '''This testcase actually works.'''
+        assert 17 == random.randint(0, 100)
+
+    When a test fails, the fixture outputs the seed used. The user can then set
+    the environment variable MXNET_TEST_SEED to the value reported, then rerun
+    the test with:
+
+        pytest --verbose -s <test_module_name.py> -k <failing_test>
+
+    To run a test repeatedly, install pytest-repeat and add the --count argument:
+
+        pip install pytest-repeat
+        pytest --verbose -s <test_module_name.py> -k <failing_test> --count 1000
+
+    """
+
+    seed = request.node.get_closest_marker('seed')
+    env_seed_str = os.getenv('MXNET_TEST_SEED')
+
+    if seed is not None:
+        seed = seed.args[0]
+        assert isinstance(seed, int)
+    elif env_seed_str is not None:
+        seed = int(env_seed_str)
+    else:
+        seed = random.randint(0, 2**31-1)
+
+    random.seed(seed)
+    try:
+        import numpy as np
+        import mxnet as mx
+        post_test_state = np.random.get_state()
+        np.random.seed(seed)
+        mx.random.seed(seed)
+    except:
+        logging.warning('Unable to import numpy/mxnet. Skipping seeding for numpy/mxnet.')
+        np = None
+
+    seed_message = ('np/mx/python random seeds are set to '
+                    '{}, use MXNET_TEST_SEED={} to reproduce.')
+    seed_message = seed_message.format(seed, seed)
+
+    # Always log seed on DEBUG log level. This makes sure we can find out the
+    # value of the seed even if the test case causes a segfault and subsequent
+    # teardown code is not run.
+    logging.debug(seed_message)
+
+    yield  # run the test
+
+    try:
+        import mxnet as mx
+        mx.nd.waitall()
+    except:
+        logging.warning('Unable to import mxnet. Skipping for mxnet engine.')
+
+    if request.node.rep_setup.failed:
+        logging.info("Setting up a test failed: {}", request.node.nodeid)
+    elif request.node.rep_call.outcome == 'failed':
+        # Either request.node.rep_setup.failed or request.node.rep_setup.passed
+        # should be True
+        assert request.node.rep_setup.passed
+        # On failure also log seed on INFO log level
+        logging.info(seed_message)
+
+    if np:
+        np.random.set_state(post_test_state)
+
+
+# * Shared test fixtures
+@pytest.fixture(params=[True, False])
+def hybridize(request):
+    return request.param
+
+@pytest.fixture(autouse=True)
+def doctest(doctest_namespace):
+    try:
+        import numpy as np
+        import mxnet as mx
+        doctest_namespace['np'] = np
+        doctest_namespace['mx'] = mx
+        doctest_namespace['gluon'] = mx.gluon
+    except:
+        logging.warning('Unable to import numpy/mxnet. Skipping conftest.')
+    import doctest
+    doctest.ELLIPSIS_MARKER = '-etc-'
diff --git a/contrib/clojure-package/examples/captcha/gen_captcha.py b/contrib/clojure-package/examples/captcha/gen_captcha.py
old mode 100755
new mode 100644
diff --git a/docker/install/python.sh b/docker/install/python.sh
index ba71246..d49b500 100755
--- a/docker/install/python.sh
+++ b/docker/install/python.sh
@@ -19,10 +19,9 @@
 
 # install libraries for mxnet's python package on ubuntu
 
-apt-get update && apt-get install -y python-dev python3-dev
+apt-get update && apt-get install -y python3-dev
 
 # the version of the pip shipped with ubuntu may be too lower, install a recent version here
-cd /tmp && wget https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py && python2 get-pip.py
+cd /tmp && wget https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py
 
-pip2 install nose pylint numpy nose-timer requests Pillow
-pip3 install nose pylint numpy nose-timer requests Pillow
+pip3 install pylint numpy requests Pillow pytest==5.3.2 pytest-env==0.6.2 pytest-cov==2.8.1 pytest-xdist==1.31.0
diff --git a/docs/static_site/src/pages/get_started/build_from_source.md b/docs/static_site/src/pages/get_started/build_from_source.md
index 1dfa95a..ae391e4 100644
--- a/docs/static_site/src/pages/get_started/build_from_source.md
+++ b/docs/static_site/src/pages/get_started/build_from_source.md
@@ -221,7 +221,7 @@ make -j"$(nproc)"
 ```
 - Run test_nccl.py script as follows. The test should complete. It does not produce any output.
 ``` bash
-nosetests --verbose tests/python/gpu/test_nccl.py
+pytest --verbose tests/python/gpu/test_nccl.py
 ```
 
 **Recommendation to get the best performance out of NCCL:**
diff --git a/example/image-classification/__init__.py b/example/image-classification/__init__.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/benchmark.py b/example/image-classification/benchmark.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/benchmark_score.py b/example/image-classification/benchmark_score.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/common/data.py b/example/image-classification/common/data.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/common/fit.py b/example/image-classification/common/fit.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/fine-tune.py b/example/image-classification/fine-tune.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/score.py b/example/image-classification/score.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/symbols/alexnet.py b/example/image-classification/symbols/alexnet.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/symbols/resnet-v1.py b/example/image-classification/symbols/resnet-v1.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/symbols/resnetv1.py b/example/image-classification/symbols/resnetv1.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/test_score.py b/example/image-classification/test_score.py
old mode 100755
new mode 100644
index 8fbac68..58c5c66
--- a/example/image-classification/test_score.py
+++ b/example/image-classification/test_score.py
@@ -25,40 +25,40 @@ from __future__ import print_function
 import mxnet as mx
 from common import find_mxnet, modelzoo
 from score import score
+import pytest
 
-VAL_DATA='data/val-5k-256.rec'
-def download_data():
-    return mx.test_utils.download(
-        'http://data.mxnet.io/data/val-5k-256.rec', VAL_DATA)
+@pytest.fixture(scope="session")
+def imagenet_val_5k_settings():
+    mx.test_utils.download(
+        'http://data.mxnet.io/data/val-5k-256.rec', 'data/val-5k-256.rec')
+    num_gpus = mx.context.num_gpus()
+    assert num_gpus > 0
+    gpus = ','.join(map(str, range(num_gpus)))
+    batch_size = 16 * num_gpus
+    kwargs = {'gpus':gpus, 'batch_size':batch_size, 'max_num_examples':500}
+    return 'data/val-5k-256.rec', kwargs
 
-def test_imagenet1k_resnet(**kwargs):
+def test_imagenet1k_resnet(imagenet_val_5k_settings):
+    imagenet_val_5k, kwargs = imagenet_val_5k_settings
     models = ['imagenet1k-resnet-50', 'imagenet1k-resnet-152']
     accs = [.77, .78]
     for (m, g) in zip(models, accs):
         acc = mx.metric.create('acc')
-        (speed,) = score(model=m, data_val=VAL_DATA,
+        (speed,) = score(model=m, data_val=imagenet_val_5k,
                          rgb_mean='0,0,0', metrics=acc, **kwargs)
         r = acc.get()[1]
         print('Tested %s, acc = %f, speed = %f img/sec' % (m, r, speed))
         assert r > g and r < g + .1
 
-def test_imagenet1k_inception_bn(**kwargs):
+def test_imagenet1k_inception_bn(imagenet_val_5k_settings):
+    imagenet_val_5k, kwargs = imagenet_val_5k_settings
     acc = mx.metric.create('acc')
     m = 'imagenet1k-inception-bn'
     g = 0.75
     (speed,) = score(model=m,
-                     data_val=VAL_DATA,
+                     data_val=imagenet_val_5k,
                      rgb_mean='123.68,116.779,103.939', metrics=acc, **kwargs)
     r = acc.get()[1]
     print('Tested %s acc = %f, speed = %f img/sec' % (m, r, speed))
     assert r > g and r < g + .1
 
-if __name__ == '__main__':
-    num_gpus = mx.context.num_gpus()
-    assert num_gpus > 0
-    batch_size = 16 * num_gpus
-    gpus = ','.join(map(str, range(num_gpus)))
-    kwargs = {'gpus':gpus, 'batch_size':batch_size, 'max_num_examples':500}
-    download_data()
-    test_imagenet1k_resnet(**kwargs)
-    test_imagenet1k_inception_bn(**kwargs)
diff --git a/example/image-classification/train_cifar10.py b/example/image-classification/train_cifar10.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/train_imagenet.py b/example/image-classification/train_imagenet.py
old mode 100755
new mode 100644
diff --git a/example/image-classification/train_mnist.py b/example/image-classification/train_mnist.py
old mode 100755
new mode 100644
diff --git a/example/neural_collaborative_filtering/ci.py b/example/neural_collaborative_filtering/ci.py
index 81f1a27..1bf5b27 100644
--- a/example/neural_collaborative_filtering/ci.py
+++ b/example/neural_collaborative_filtering/ci.py
@@ -14,13 +14,13 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-# 
+
 import mxnet as mx
 from core.model import get_model
 
 def test_model():
     def test_ncf(model_type):
-        net = get_model(model_type=model_type, factor_size_mlp=128, factor_size_gmf=64, 
+        net = get_model(model_type=model_type, factor_size_mlp=128, factor_size_gmf=64,
                         model_layers=[256, 128, 64], num_hidden=1, max_user=138493, max_item=26744)
         mod = mx.module.Module(net, context=mx.cpu(), data_names=['user', 'item'], label_names=['softmax_label'])
         provide_data = [mx.io.DataDesc(name='item', shape=((1,))),
@@ -58,7 +58,3 @@ def test_model():
     for model_type in ['neumf', 'mlp', 'gmf']:
         test_ncf(model_type)
 
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
-
diff --git a/example/reinforcement-learning/dqn/dqn_demo.py b/example/reinforcement-learning/dqn/dqn_demo.py
old mode 100755
new mode 100644
diff --git a/example/reinforcement-learning/dqn/dqn_run_test.py b/example/reinforcement-learning/dqn/dqn_run_test.py
old mode 100755
new mode 100644
diff --git a/example/ssd/data/demo/download_demo_images.py b/example/ssd/data/demo/download_demo_images.py
old mode 100755
new mode 100644
diff --git a/example/ssd/dataset/pycocotools/__init__.py b/example/ssd/dataset/pycocotools/__init__.py
old mode 100755
new mode 100644
diff --git a/example/ssd/dataset/pycocotools/coco.py b/example/ssd/dataset/pycocotools/coco.py
old mode 100755
new mode 100644
diff --git a/example/ssd/demo.py b/example/ssd/demo.py
old mode 100755
new mode 100644
diff --git a/example/ssd/tools/prepare_dataset.py b/example/ssd/tools/prepare_dataset.py
old mode 100755
new mode 100644
diff --git a/example/ssd/train.py b/example/ssd/train.py
old mode 100755
new mode 100644
diff --git a/docker/install/python.sh b/pytest.ini
old mode 100755
new mode 100644
similarity index 63%
copy from docker/install/python.sh
copy to pytest.ini
index ba71246..52ad95f
--- a/docker/install/python.sh
+++ b/pytest.ini
@@ -1,5 +1,3 @@
-#!/usr/bin/env bash
-
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -17,12 +15,16 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# install libraries for mxnet's python package on ubuntu
-
-apt-get update && apt-get install -y python-dev python3-dev
+[pytest]
+markers =
+    seed: set the python, numpy and mxnet random seeds to a specified value for test reproducibility
+    serial: mark a test that requires more resources to run that are thus only suitable for serial run.
+    remote_required: mark a test that requires internet access.
+    gpu: mark a test that requires GPU.
+    integration: mark an integration test
+    onnx_coverage: ONNX coverage test
 
-# the version of the pip shipped with ubuntu may be too lower, install a recent version here
-cd /tmp && wget https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py && python2 get-pip.py
+env =
+    MXNET_HOME=tests/data
 
-pip2 install nose pylint numpy nose-timer requests Pillow
-pip3 install nose pylint numpy nose-timer requests Pillow
+timeout = 1200
diff --git a/python/README.md b/python/README.md
index 1481885..e5d2faa 100644
--- a/python/README.md
+++ b/python/README.md
@@ -25,14 +25,14 @@ To install MXNet Python package, visit MXNet [Install Instruction](https://mxnet
 
 ## Running the unit tests
 
-For running unit tests, you will need the [nose PyPi package](https://pypi.python.org/pypi/nose). To install:
+For running unit tests, you will need the [pytest PyPi package](https://pypi.python.org/pypi/pytest). To install:
 ```bash
-pip install --upgrade nose
+pip install --upgrade pytest
 ```
 
-Once ```nose``` is installed, run the following from MXNet root directory (please make sure the installation path of ```nosetests``` is included in your ```$PATH``` environment variable):
+Once ```pytest``` is installed, run the following from MXNet root directory (please make sure the installation path of ```pytest``` is included in your ```$PATH``` environment variable):
 ```
-nosetests tests/python/unittest
-nosetests tests/python/train
+pytest tests/python/unittest
+pytest tests/python/train
 
 ```
diff --git a/python/mxnet/_ffi/base.py b/python/mxnet/_ffi/base.py
index be68d20..df95b95 100644
--- a/python/mxnet/_ffi/base.py
+++ b/python/mxnet/_ffi/base.py
@@ -69,18 +69,3 @@ def c_array(ctype, values):
         Created ctypes array
     """
     return (ctype * len(values))(*values)
-
-
-def decorate(func, fwrapped):
-    """A wrapper call of decorator package, differs to call time
-
-    Parameters
-    ----------
-    func : function
-        The original function
-
-    fwrapped : function
-        The wrapped function
-    """
-    import decorator
-    return decorator.decorate(func, fwrapped)
diff --git a/python/mxnet/contrib/amp/amp.py b/python/mxnet/contrib/amp/amp.py
old mode 100755
new mode 100644
diff --git a/python/mxnet/contrib/amp/loss_scaler.py b/python/mxnet/contrib/amp/loss_scaler.py
old mode 100755
new mode 100644
diff --git a/python/mxnet/image/detection.py b/python/mxnet/image/detection.py
index 52c9468..e62fb67 100644
--- a/python/mxnet/image/detection.py
+++ b/python/mxnet/image/detection.py
@@ -702,7 +702,7 @@ class ImageDetIter(ImageIter):
 
     def _estimate_label_shape(self):
         """Helper function to estimate label shape"""
-        max_count = 0
+        max_count, label = 0, None
         self.reset()
         try:
             while True:
@@ -712,7 +712,7 @@ class ImageDetIter(ImageIter):
         except StopIteration:
             pass
         self.reset()
-        return (max_count, label.shape[1])
+        return (max_count, label.shape[1] if label is not None else 5)
 
     def _parse_label(self, label):
         """Helper function to parse object detection label.
diff --git a/python/mxnet/initializer.py b/python/mxnet/initializer.py
old mode 100755
new mode 100644
diff --git a/python/mxnet/module/executor_group.py b/python/mxnet/module/executor_group.py
old mode 100755
new mode 100644
diff --git a/python/mxnet/optimizer/optimizer.py b/python/mxnet/optimizer/optimizer.py
old mode 100755
new mode 100644
diff --git a/python/mxnet/test_utils.py b/python/mxnet/test_utils.py
old mode 100755
new mode 100644
index 6a2c245..6129d69
--- a/python/mxnet/test_utils.py
+++ b/python/mxnet/test_utils.py
@@ -756,24 +756,6 @@ def assert_exception(f, exception_type, *args, **kwargs):
     except exception_type:
         return
 
-def retry(n):
-    """Retry n times before failing for stochastic test cases."""
-    assert n > 0
-    def decorate(f):
-        """Decorate a test case."""
-        def wrapper(*args, **kwargs):
-            """Wrapper for tests function."""
-            for _ in range(n):
-                try:
-                    f(*args, **kwargs)
-                except AssertionError as e:
-                    err = e
-                    continue
-                return
-            raise err
-        return wrapper
-    return decorate
-
 
 def simple_forward(sym, ctx=None, is_train=False, **inputs):
     """A simple forward function for a symbol.
diff --git a/python/setup.py b/python/setup.py
index ccfccb3..a040cbb 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -30,7 +30,7 @@ if "--inplace" in sys.argv:
 else:
     from setuptools import setup
     from setuptools.extension import Extension
-    kwargs = {'install_requires': ['numpy>1.16.0,<2.0.0', 'requests>=2.20.0,<3', 'graphviz<0.9.0,>=0.8.1'], 'zip_safe': False}
+    kwargs = {'install_requires': ['numpy>=1.17', 'requests>=2.20.0,<3', 'graphviz<0.9.0,>=0.8.1'], 'zip_safe': False}
 
 with_cython = False
 if '--with-cython' in sys.argv:
diff --git a/snapcraft.yaml b/snapcraft.yaml
deleted file mode 100644
index 896db37..0000000
--- a/snapcraft.yaml
+++ /dev/null
@@ -1,79 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-name: mxnet
-version: '2.0.0'
-summary: MXNet is a deep learning framework designed for efficiency and flexibility.
-description: |
-  MXNet is a deep learning framework designed for both efficiency and 
-  flexibility. It allows you to mix the flavours of symbolic programming and 
-  imperative programming to maximize efficiency and productivity. In its core, 
-  a dynamic dependency scheduler that automatically parallelizes both symbolic 
-  and imperative operations on the fly. A graph optimization layer on top of 
-  that makes symbolic execution fast and memory efficient. The library is 
-  portable and lightweight, and it scales to multiple GPUs and multiple machines.
-
-grade: stable
-confinement: strict
-
-apps:
-  python:
-    command: snap.python
-
-parts:
-  mxnet:
-    source: .
-    plugin: make
-    build-packages:
-      - build-essential
-      - libatlas-base-dev
-      - libopencv-dev
-    stage-packages:
-      - libatlas3-base
-      - libopencv-calib3d2.4v5
-      - libopencv-core2.4v5
-      - libopencv-highgui2.4v5
-      - libopencv-imgproc2.4v5
-      - libopencv-ml2.4v5
-      - libopencv-objdetect2.4v5
-    prepare: |
-      cp make/config.mk .
-    build: |
-      make
-    install: |
-      cp -r bin $SNAPCRAFT_PART_INSTALL/
-      cp -r lib $SNAPCRAFT_PART_INSTALL/
-    
-  mxnet-ubuntu-python:
-    plugin: python
-    python-version: python2
-    source: ./python
-    stage-packages:
-      - python-numpy
-    python-packages:
-      - graphviz
-      - Jupyter
-    after: [mxnet]
-    
-  python-wrapper:
-    plugin: dump
-    source: .
-    stage:
-      - snap.python
-    prime:
-      - snap.python
-
diff --git a/tests/README.md b/tests/README.md
index de5d810..b59335e 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -68,7 +68,7 @@ Ninja is a build tool (like make) that prioritizes building speed. If you will b
     ```
 An example for running python tests would be
 ```
-ci/build.py --platform build_ubuntu_cpu_mkldnn /work/runtime_functions.sh unittest_ubuntu_python3_cpu PYTHONPATH=./python/ nosetests-2.7 tests/python/unittest
+ci/build.py --platform build_ubuntu_cpu_mkldnn /work/runtime_functions.sh unittest_ubuntu_python3_cpu PYTHONPATH=./python/ pytest tests/python/unittest
 ```
 
 
diff --git a/tests/jenkins/run_test.sh b/tests/jenkins/run_test.sh
index 5ded742..48bb4da 100755
--- a/tests/jenkins/run_test.sh
+++ b/tests/jenkins/run_test.sh
@@ -42,16 +42,10 @@ export MXNET_ENGINE_INFO=false
 export PYTHONPATH=$(pwd)/python
 
 echo "BUILD python_test"
-nosetests --verbose tests/python/unittest || exit -1
-nosetests --verbose tests/python/gpu/test_operator_gpu.py || exit -1
-nosetests --verbose tests/python/gpu/test_forward.py || exit -1
-nosetests --verbose tests/python/train || exit -1
-
-echo "BUILD python3_test"
-nosetests3 --verbose tests/python/unittest || exit -1
-nosetests3 --verbose tests/python/gpu/test_operator_gpu.py || exit -1
-nosetests3 --verbose tests/python/gpu/test_forward.py || exit -1
-nosetests3 --verbose tests/python/train || exit -1
+pytest --verbose tests/python/unittest || exit -1
+pytest --verbose tests/python/gpu/test_operator_gpu.py || exit -1
+pytest --verbose tests/python/gpu/test_forward.py || exit -1
+pytest --verbose tests/python/train || exit -1
 
 echo "BUILD scala_test"
 export PATH=$PATH:/opt/apache-maven/bin
diff --git a/tests/jenkins/run_test_amzn_linux_gpu.sh b/tests/jenkins/run_test_amzn_linux_gpu.sh
index 57d9c78..a257b96 100755
--- a/tests/jenkins/run_test_amzn_linux_gpu.sh
+++ b/tests/jenkins/run_test_amzn_linux_gpu.sh
@@ -53,12 +53,8 @@ export MXNET_ENGINE_INFO=false
 export PYTHONPATH=${PWD}/python
 
 echo "BUILD python_test"
-nosetests --verbose tests/python/unittest
-nosetests --verbose tests/python/train
-
-echo "BUILD python3_test"
-nosetests3 --verbose tests/python/unittest
-nosetests3 --verbose tests/python/train
+pytest --verbose tests/python/unittest
+pytest --verbose tests/python/train
 
 #echo "BUILD julia_test"
 #export MXNET_HOME="${PWD}"
diff --git a/tests/jenkins/run_test_ubuntu.sh b/tests/jenkins/run_test_ubuntu.sh
index 0459d2c..9c3d3c5 100755
--- a/tests/jenkins/run_test_ubuntu.sh
+++ b/tests/jenkins/run_test_ubuntu.sh
@@ -54,16 +54,10 @@ make -j$(nproc)
 export PYTHONPATH=${PWD}/python
 
 echo "BUILD python_test"
-nosetests --verbose tests/python/unittest || exit 1
-nosetests --verbose tests/python/gpu/test_operator_gpu.py || exit 1
-nosetests --verbose tests/python/gpu/test_forward.py || exit 1
-nosetests --verbose tests/python/train || exit 1
-
-echo "BUILD python3_test"
-nosetests3 --verbose tests/python/unittest || exit 1
-nosetests3 --verbose tests/python/gpu/test_operator_gpu.py || exit 1
-nosetests3 --verbose tests/python/gpu/test_forward.py || exit 1
-nosetests3 --verbose tests/python/train || exit 1
+pytest --verbose tests/python/unittest || exit 1
+pytest --verbose tests/python/gpu/test_operator_gpu.py || exit 1
+pytest --verbose tests/python/gpu/test_forward.py || exit 1
+pytest --verbose tests/python/train || exit 1
 
 echo "BUILD scala_test"
 export PATH=$PATH:/opt/apache-maven/bin
diff --git a/tests/nightly/broken_link_checker_test/test_broken_links.py b/tests/nightly/broken_link_checker_test/test_broken_links.py
old mode 100755
new mode 100644
diff --git a/tests/nightly/compilation_warnings/process_output.py b/tests/nightly/compilation_warnings/process_output.py
old mode 100755
new mode 100644
diff --git a/tests/nightly/dist_device_sync_kvstore.py b/tests/nightly/dist_device_sync_kvstore.py
index 26e665d..a696876 100644
--- a/tests/nightly/dist_device_sync_kvstore.py
+++ b/tests/nightly/dist_device_sync_kvstore.py
@@ -127,5 +127,6 @@ def test_gluon_trainer_type():
 
 if __name__ == "__main__":
     test_sync_init()
-    test_sync_push_pull()
+    # TODO(szha): disabled due to repeated failures. tracked in #18098
+    # test_sync_push_pull()
     test_gluon_trainer_type()
diff --git a/tests/nightly/estimator/test_estimator_cnn.py b/tests/nightly/estimator/test_estimator_cnn.py
index af51953..0d113cd 100644
--- a/tests/nightly/estimator/test_estimator_cnn.py
+++ b/tests/nightly/estimator/test_estimator_cnn.py
@@ -155,7 +155,3 @@ def test_estimator_gpu():
 
     assert acc.get()[1] > 0.80
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/nightly/estimator/test_sentiment_rnn.py b/tests/nightly/estimator/test_sentiment_rnn.py
index ab124ba..367c69b 100644
--- a/tests/nightly/estimator/test_sentiment_rnn.py
+++ b/tests/nightly/estimator/test_sentiment_rnn.py
@@ -280,7 +280,3 @@ def test_estimator_gpu():
 
     assert acc.get()[1] > 0.70
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/nightly/test_distributed_training-gpu.sh b/tests/nightly/test_distributed_training-gpu.sh
index 40b6e14..319c031 100755
--- a/tests/nightly/test_distributed_training-gpu.sh
+++ b/tests/nightly/test_distributed_training-gpu.sh
@@ -27,14 +27,14 @@ test_kvstore() {
         "-n 4 --launcher local python3 dist_device_sync_kvstore.py"
         "-n 4 --launcher local python3 dist_device_sync_kvstore_custom.py"
         "--p3 -n 4 --launcher local python3 dist_device_sync_kvstore_custom.py"
-        "-n 4 --launcher local python3 dist_sync_kvstore.py --type=init_gpu" 
+        "-n 4 --launcher local python3 dist_sync_kvstore.py --type=init_gpu"
     )
 
     for arg in "${test_args[@]}"; do
         python3 ../../tools/launch.py $arg
         if [ $? -ne 0 ]; then
             return $?
-        fi 
+        fi
     done
 }
 
@@ -50,4 +50,4 @@ test_horovod() {
 test_kvstore
 test_horovod
 
-exit $errors
\ No newline at end of file
+exit $errors
diff --git a/tests/nightly/test_large_array.py b/tests/nightly/test_large_array.py
index 5fb0ff8..0f0b373 100644
--- a/tests/nightly/test_large_array.py
+++ b/tests/nightly/test_large_array.py
@@ -28,7 +28,6 @@ sys.path.append(os.path.join(curr_path, '../python/unittest/'))
 from mxnet.test_utils import rand_ndarray, assert_almost_equal, rand_coord_2d, default_context, check_symbolic_forward, create_2d_tensor
 from mxnet import gluon, nd
 from common import with_seed, with_post_test_cleanup
-from nose.tools import with_setup
 import unittest
 
 # dimension constants
@@ -469,7 +468,7 @@ def test_nn():
         assert res.shape[2] == 2
         assert res.shape[3] == 2
         assert res.shape[4] == 1
-        
+
     def check_embedding():
         data = nd.random_normal(shape=(LARGE_TENSOR_SHAPE, 1))
         weight = nd.random_normal(shape=(LARGE_TENSOR_SHAPE, 1))
@@ -480,7 +479,7 @@ def test_nn():
 
         assert out.shape[0] == LARGE_TENSOR_SHAPE
         assert out.shape[1] == 1
-        
+
     def check_spatial_transformer():
         data = nd.random_normal(shape=(2, 2**29, 1, 6))
         loc = nd.random_normal(shape=(2, 6))
@@ -495,7 +494,7 @@ def test_nn():
         assert res.shape[1] == 536870912
         assert res.shape[2] == 2
         assert res.shape[3] == 6
-        
+
     def check_ravel():
         data = nd.random_normal(shape=(2, LARGE_TENSOR_SHAPE))
         shape = (2, 10)
@@ -530,7 +529,7 @@ def test_nn():
 
         # Trigger lazy evaluation of the output NDArray and ensure that it has been filled
         assert type(out[0, 0].asscalar()).__name__ == 'float32'
-        
+
     def check_rnn():
         data = nd.random_normal(shape=(RNN_LARGE_TENSOR, 4, 4))
         parameters_relu_tanh = nd.random_normal(shape=(7,))
@@ -547,10 +546,10 @@ def test_nn():
 
         out_relu = nd.RNN(data=data, parameters=parameters_relu_tanh, state=state, mode=mode_relu,
                           state_size=state_size, num_layers=num_layers)
-        
+
         out_tanh = nd.RNN(data=data, parameters=parameters_relu_tanh, state=state, mode=mode_tanh,
                           state_size=state_size, num_layers=num_layers)
-        
+
         out_lstm = nd.RNN(data=data, parameters=parameters_lstm, state=state, mode=mode_lstm,
                           state_cell=state_cell, state_size=state_size, num_layers=num_layers)
 
@@ -1546,7 +1545,7 @@ def test_basic():
     def create_input_for_rounding_ops():
         # Creates an vector with values (-LARGE_X/2 .... -2, -1, 0, 1, 2, .... , LARGE_X/2-1)
         # then divides each element by 2 i.e (-LARGE_X/4 .... -1, -0.5, 0, 0.5, 1, .... , LARGE_X/4-1)
-        # and finally broadcasts to 
+        # and finally broadcasts to
         inp = nd.arange(-LARGE_X//2, LARGE_X//2, dtype=np.float64).reshape(1, LARGE_X)
         inp = inp/2
         inp = nd.broadcast_to(inp, (SMALL_Y, LARGE_X))
@@ -1559,7 +1558,7 @@ def test_basic():
         for i in range(len(output_idx_to_inspect)):
             assert output[1][output_idx_to_inspect[i]] == expected_vals[i]
 
-    # TODO(access2rohit): merge similar tests in large vector and array into one file. 
+    # TODO(access2rohit): merge similar tests in large vector and array into one file.
     def check_rounding_ops():
         x = create_input_for_rounding_ops()
         def check_ceil():
@@ -1819,7 +1818,3 @@ def test_sparse_dot():
     assert out.asnumpy()[0][0] == 2
     assert out.shape == (2, 2)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/nightly/test_large_vector.py b/tests/nightly/test_large_vector.py
index bbad756..57fecaa 100644
--- a/tests/nightly/test_large_vector.py
+++ b/tests/nightly/test_large_vector.py
@@ -28,7 +28,6 @@ sys.path.append(os.path.join(curr_path, '../python/unittest/'))
 from mxnet.test_utils import rand_ndarray, assert_almost_equal, rand_coord_2d, create_vector
 from mxnet import gluon, nd
 from tests.python.unittest.common import with_seed
-from nose.tools import with_setup
 import unittest
 
 # dimension constants
@@ -370,7 +369,7 @@ def test_tensor():
 
     def check_gather():
         arr = mx.nd.ones(LARGE_X)
-        # Passing dtype=np.int64 since randomly generated indices are 
+        # Passing dtype=np.int64 since randomly generated indices are
         # very large that exceeds int32 limits.
         idx = mx.nd.random.randint(0, LARGE_X, 10, dtype=np.int64)
         # Calls gather_nd internally
@@ -1063,7 +1062,3 @@ def test_basic():
     check_maximum()
     check_minimum()
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/nightly/test_np_random.py b/tests/nightly/test_np_random.py
index 09ebdad..0f7ca9c 100644
--- a/tests/nightly/test_np_random.py
+++ b/tests/nightly/test_np_random.py
@@ -31,8 +31,8 @@ import numpy as _np
 import mxnet as mx
 from mxnet import np, npx, autograd
 from mxnet.gluon import HybridBlock
-from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndarray, retry, use_np
-from common import with_seed
+from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndarray, use_np
+from common import with_seed, retry
 from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, assert_exception, is_op_runnable, collapse_sum_like
 from mxnet.ndarray.ndarray import py_slice
 from mxnet.base import integer_types
@@ -173,7 +173,3 @@ def test_np_laplace():
             generator_mx_np = lambda x: np.random.laplace(loc, scale, size=x, ctx=ctx, dtype=dtype).asnumpy()
             verify_generator(generator=generator_mx_np, buckets=buckets, probs=probs, nsamples=samples, nrepeat=trials)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/nightly/test_optimizer.py b/tests/nightly/test_optimizer.py
index c4e264b..0a87368 100644
--- a/tests/nightly/test_optimizer.py
+++ b/tests/nightly/test_optimizer.py
@@ -88,6 +88,3 @@ def test_lars():
     accuracy = acc.get()[1]
     assert accuracy > 0.98, "LeNet-5 training accuracy on MNIST was too low"
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_contrib_amp.py b/tests/python/gpu/test_contrib_amp.py
index 527f853..b7aeeb7 100644
--- a/tests/python/gpu/test_contrib_amp.py
+++ b/tests/python/gpu/test_contrib_amp.py
@@ -24,19 +24,27 @@ import warnings
 import collections
 import ctypes
 import mxnet.contrib.amp as amp
-from nose.tools import assert_raises
+import pytest
 from mxnet.test_utils import set_default_context, download_model, same_symbol_structure
 from mxnet.gluon.model_zoo.vision import get_model
 from mxnet.gluon import SymbolBlock, nn, rnn
 from mxnet.contrib.amp import amp
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import with_seed, teardown, assert_raises_cudnn_not_satisfied
+from common import with_seed, teardown_module, assert_raises_cudnn_not_satisfied
 sys.path.insert(0, os.path.join(curr_path, '../train'))
 from test_bucketing import train_model
 set_default_context(mx.gpu(0))
 
-def test_amp_coverage():
+@pytest.fixture()
+def amp_tests(request):
+    def teardown():
+        mx.nd.waitall()
+
+    request.addfinalizer(teardown)
+
+@pytest.mark.skip(reason='Error during waitall(). Tracked in #18099')
+def test_amp_coverage(amp_tests):
     conditional = [item[0] for item in amp.lists.symbol_fp16.CONDITIONAL_FP32_FUNCS]
 
     # Check for duplicates
@@ -96,8 +104,9 @@ def test_amp_coverage():
                        - If you are not sure which list to choose, FP32_FUNCS is the
                          safest option""")
 
+@pytest.mark.skip(reason='Error during waitall(). Tracked in #18099')
 @with_seed()
-def test_amp_conversion():
+def test_amp_conversion(amp_tests):
     def check_amp_convert_symbol():
         x = mx.sym.var("x")
         y = mx.sym.var("y")
@@ -119,18 +128,18 @@ def test_amp_conversion():
             "convert_symbol generating wrong computation graph"
 
         # convert_symbol called with incorrect inputs
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="float16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["elemwise_add"])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="float16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["Activation"],
                       conditional_fp32_ops=[('Activation', 'act_type', ['selu'])])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="float16", target_dtype_ops=["Activation"],
                       fp32_ops=["Activation"],
                       conditional_fp32_ops=[('Activation', 'act_type', ['selu'])])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="float16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["FullyConnected"])
 
@@ -345,8 +354,9 @@ def test_amp_conversion():
         check_amp_convert_bucketing_module()
 
 @with_seed()
+@pytest.mark.skip(reason='Error during waitall(). Tracked in #18099')
 @assert_raises_cudnn_not_satisfied(min_version='5.1.10')
-def test_amp_conversion_rnn():
+def test_amp_conversion_rnn(amp_tests):
     with mx.Context(mx.gpu(0)):
         model = nn.HybridSequential()
         model.add(rnn.LSTM(hidden_size=10, num_layers=2, bidirectional=True))
@@ -360,7 +370,8 @@ def test_amp_conversion_rnn():
 
 
 @with_seed()
-def test_module_backward_compatibility():
+@pytest.mark.skip(reason='Error during waitall(). Tracked in #18099')
+def test_module_backward_compatibility(amp_tests):
     channel_num = 10
     conv_layer_filter_dims = [2, 3]
     conv_layer_strides = [1, 1]
@@ -403,7 +414,8 @@ def test_module_backward_compatibility():
 
 
 @with_seed()
-def test_fp16_casting():
+@pytest.mark.skip(reason='Error during waitall(). Tracked in #18099')
+def test_fp16_casting(amp_tests):
     data = mx.sym.var("data")
     out1 = mx.sym.amp_cast(data, dtype="float16")
     out2 = mx.sym.amp_cast(data, dtype="float32")
@@ -484,7 +496,3 @@ def test_fp16_casting():
     out = mx.sym.split(concat_res, axis=1, num_outputs=2)
     final_res = amp.convert_symbol(out)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_deferred_compute_gpu.py b/tests/python/gpu/test_deferred_compute_gpu.py
index 7503c7b..9802d2b 100644
--- a/tests/python/gpu/test_deferred_compute_gpu.py
+++ b/tests/python/gpu/test_deferred_compute_gpu.py
@@ -24,10 +24,6 @@ mx.test_utils.set_default_context(mx.gpu(0))
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
 # We import all tests from ../unittest/test_deferred_compute.py
-# They will be detected by nose, as long as the current file has a different filename
+# They will be detected by test framework, as long as the current file has a different filename
 from test_deferred_compute import *
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_forward.py b/tests/python/gpu/test_forward.py
index 02b0256..2ec5ee2 100644
--- a/tests/python/gpu/test_forward.py
+++ b/tests/python/gpu/test_forward.py
@@ -22,7 +22,7 @@ import mxnet as mx
 from mxnet.test_utils import *
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.gluon import utils
 import tarfile
 
diff --git a/tests/python/gpu/test_fusion.py b/tests/python/gpu/test_fusion.py
index a6be6c7..eb39fb1 100644
--- a/tests/python/gpu/test_fusion.py
+++ b/tests/python/gpu/test_fusion.py
@@ -337,6 +337,3 @@ def test_fusion_reshape_executor():
     out = f.forward(is_train=False, data1=data, data2=data)
     assert out[0].sum().asscalar() == 150
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_gluon_contrib_gpu.py b/tests/python/gpu/test_gluon_contrib_gpu.py
index 348e9f7..9b43c79 100644
--- a/tests/python/gpu/test_gluon_contrib_gpu.py
+++ b/tests/python/gpu/test_gluon_contrib_gpu.py
@@ -85,6 +85,3 @@ def test_ModulatedDeformableConvolution():
         y = net(x)
         y.backward()
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_gluon_gpu.py b/tests/python/gpu/test_gluon_gpu.py
index 7e90854..b379614 100644
--- a/tests/python/gpu/test_gluon_gpu.py
+++ b/tests/python/gpu/test_gluon_gpu.py
@@ -27,10 +27,11 @@ import mxnet.ndarray as nd
 import numpy as np
 import math
 from mxnet import autograd
+import pytest
 
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown, assert_raises_cudnn_not_satisfied, run_in_spawned_process
+from common import setup_module, with_seed, teardown_module, assert_raises_cudnn_not_satisfied, run_in_spawned_process
 from test_gluon import *
 from test_loss import *
 from test_gluon_rnn import *
@@ -597,7 +598,7 @@ def test_hybridblock_mix_ctx_raise():
             return a + b
     foo_hybrid = FooHybrid()
     foo_hybrid.hybridize()
-    assert_raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,), ctx=mx.gpu()),
+    pytest.raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,), ctx=mx.gpu()),
                                                  mx.nd.ones((10,), ctx=mx.cpu())))
 
 @with_seed()
@@ -639,7 +640,3 @@ def test_gemms_true_fp16():
                         atol=atol, rtol=rtol)
     os.environ["MXNET_FC_TRUE_FP16"] = "0"
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_gluon_model_zoo_gpu.py b/tests/python/gpu/test_gluon_model_zoo_gpu.py
index 6f559db..6cb5bcf 100644
--- a/tests/python/gpu/test_gluon_model_zoo_gpu.py
+++ b/tests/python/gpu/test_gluon_model_zoo_gpu.py
@@ -25,9 +25,10 @@ from mxnet.test_utils import assert_almost_equal
 import sys
 import os
 import unittest
+import pytest
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 def eprint(*args, **kwargs):
     print(*args, file=sys.stderr, **kwargs)
@@ -38,60 +39,57 @@ def download_data():
         'http://data.mxnet.io/data/val-5k-256.rec', VAL_DATA)
 
 @with_seed()
-def test_inference():
-    all_models = ['resnet50_v1', 'vgg19_bn', 'alexnet', #'inceptionv3',
-                  'densenet201', 'squeezenet1.0', 'mobilenet0.25']
-
+@pytest.mark.parametrize('model_name', ['resnet50_v1', 'vgg19_bn', 'alexnet', 'densenet201', 'squeezenet1.0', 'mobilenet0.25'])
+def test_inference(model_name):
     batch_size = 10
     download_data()
-    for model_name in all_models:
-        eprint('testing inference on %s'%model_name)
-
-        data_shape = (3, 224, 224) if 'inception' not in model_name else (3, 299, 299)
-        dataIter = mx.io.ImageRecordIter(
-            path_imgrec        = VAL_DATA,
-            label_width        = 1,
-            preprocess_threads = 1,
-            batch_size         = batch_size,
-            data_shape         = data_shape,
-            label_name         = 'softmax_label',
-            rand_crop          = False,
-            rand_mirror        = False)
-        data_batch = dataIter.next()
-        data = data_batch.data[0]
-        label = data_batch.label[0]
-        gpu_data = data.as_in_context(mx.gpu())
-        gpu_label = label.as_in_context(mx.gpu())
-
-        # This is to create a model and run the model once to initialize
-        # all parameters.
-        cpu_model = get_model(model_name)
-        cpu_model.collect_params().initialize(ctx=mx.cpu())
-        cpu_model(mx.nd.array(data, ctx=mx.cpu()))
-        gpu_model = get_model(model_name)
-        gpu_model.collect_params().initialize(ctx=mx.gpu())
-        gpu_model(mx.nd.array(data, ctx=mx.gpu()))
+    eprint('testing inference on %s'%model_name)
 
-        # Force the two models have the same parameters.
-        cpu_params = cpu_model.collect_params()
-        gpu_params = gpu_model.collect_params()
-        for k in cpu_params.keys():
-            k = k.replace(cpu_params.prefix, '')
-            cpu_param = cpu_params.get(k)
-            gpu_param = gpu_params.get(k)
-            gpu_param.set_data(cpu_param.data().as_in_context(mx.gpu()))
+    data_shape = (3, 224, 224) if 'inception' not in model_name else (3, 299, 299)
+    dataIter = mx.io.ImageRecordIter(
+        path_imgrec        = VAL_DATA,
+        label_width        = 1,
+        preprocess_threads = 1,
+        batch_size         = batch_size,
+        data_shape         = data_shape,
+        label_name         = 'softmax_label',
+        rand_crop          = False,
+        rand_mirror        = False)
+    data_batch = dataIter.next()
+    data = data_batch.data[0]
+    label = data_batch.label[0]
+    gpu_data = data.as_in_context(mx.gpu())
+    gpu_label = label.as_in_context(mx.gpu())
 
-        cpu_data = mx.nd.array(data, ctx=mx.cpu())
-        for i in range(5):
-            # Run inference.
-            with autograd.record(train_mode=False):
-                cpu_out = cpu_model(cpu_data)
-                gpu_out = gpu_model(gpu_data)
+    # This is to create a model and run the model once to initialize
+    # all parameters.
+    cpu_model = get_model(model_name)
+    cpu_model.collect_params().initialize(ctx=mx.cpu())
+    cpu_model(mx.nd.array(data, ctx=mx.cpu()))
+    gpu_model = get_model(model_name)
+    gpu_model.collect_params().initialize(ctx=mx.gpu())
+    gpu_model(mx.nd.array(data, ctx=mx.gpu()))
+
+    # Force the two models have the same parameters.
+    cpu_params = cpu_model.collect_params()
+    gpu_params = gpu_model.collect_params()
+    for k in cpu_params.keys():
+        k = k.replace(cpu_params.prefix, '')
+        cpu_param = cpu_params.get(k)
+        gpu_param = gpu_params.get(k)
+        gpu_param.set_data(cpu_param.data().as_in_context(mx.gpu()))
+
+    cpu_data = mx.nd.array(data, ctx=mx.cpu())
+    for i in range(5):
+        # Run inference.
+        with autograd.record(train_mode=False):
+            cpu_out = cpu_model(cpu_data)
+            gpu_out = gpu_model(gpu_data)
 
-            max_val = np.max(np.abs(cpu_out.asnumpy()))
-            gpu_max_val = np.max(np.abs(gpu_out.asnumpy()))
-            eprint(model_name + ": CPU " + str(max_val) + ", GPU " + str(gpu_max_val))
-            assert_almost_equal(cpu_out / max_val, gpu_out / gpu_max_val, rtol=1e-3, atol=1e-3)
+        max_val = np.max(np.abs(cpu_out.asnumpy()))
+        gpu_max_val = np.max(np.abs(gpu_out.asnumpy()))
+        eprint(model_name + ": CPU " + str(max_val) + ", GPU " + str(gpu_max_val))
+        assert_almost_equal(cpu_out / max_val, gpu_out / gpu_max_val, rtol=1e-3, atol=1e-3)
 
 def get_nn_model(name):
     if "densenet" in name:
@@ -180,6 +178,3 @@ def test_training():
                 gpu_param = gpu_params.get(k)
                 assert_almost_equal(cpu_param.data(), gpu_param.data(), rtol=1e-3, atol=1e-3)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_gluon_transforms.py b/tests/python/gpu/test_gluon_transforms.py
index e777f0b..23addbf 100644
--- a/tests/python/gpu/test_gluon_transforms.py
+++ b/tests/python/gpu/test_gluon_transforms.py
@@ -27,7 +27,7 @@ from mxnet.test_utils import assert_almost_equal, set_default_context
 from mxnet.test_utils import almost_equal, same
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import assertRaises, setup_module, with_seed, teardown
+from common import assertRaises, setup_module, with_seed, teardown_module
 from test_gluon_data_vision import test_to_tensor, test_normalize, test_crop_resize
 
 set_default_context(mx.gpu(0))
diff --git a/tests/python/gpu/test_kvstore_gpu.py b/tests/python/gpu/test_kvstore_gpu.py
index a986f70..f83220a 100644
--- a/tests/python/gpu/test_kvstore_gpu.py
+++ b/tests/python/gpu/test_kvstore_gpu.py
@@ -24,7 +24,7 @@ import unittest
 from mxnet.test_utils import assert_almost_equal, default_context, EnvManager
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 shape = (4, 4)
 keys = [5, 7, 11]
@@ -40,12 +40,12 @@ def init_kv_with_str(stype='default', kv_type='local'):
     return kv
 
 # 1. Test seed 89411477 (module seed 1829754103) resulted in a py3-gpu CI runner core dump.
-# 2. Test seed 1155716252 (module seed 1032824746) resulted in py3-mkldnn-gpu have error 
+# 2. Test seed 1155716252 (module seed 1032824746) resulted in py3-mkldnn-gpu have error
 # src/operator/nn/mkldnn/mkldnn_base.cc:567: Check failed: similar
 # Both of them are not reproducible, so this test is back on random seeds.
 @with_seed()
 @unittest.skipIf(mx.context.num_gpus() < 2, "test_rsp_push_pull needs more than 1 GPU")
-@unittest.skip("Flaky test https://github.com/apache/incubator-mxnet/issues/14189") 
+@unittest.skip("Flaky test https://github.com/apache/incubator-mxnet/issues/14189")
 def test_rsp_push_pull():
     def check_rsp_push_pull(kv_type, sparse_pull, is_push_cpu=True):
         kv = init_kv_with_str('row_sparse', kv_type)
@@ -134,6 +134,3 @@ def test_rsp_push_pull_large_rowid():
     kv.row_sparse_pull('a', out=out, row_ids=mx.nd.arange(0, num_rows, dtype='int64'))
     assert(out.indices.shape[0] == num_rows)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_numpy_fallback.py b/tests/python/gpu/test_numpy_fallback.py
index 1499e1b..71ca09c 100644
--- a/tests/python/gpu/test_numpy_fallback.py
+++ b/tests/python/gpu/test_numpy_fallback.py
@@ -26,7 +26,6 @@ import platform
 import mxnet as mx
 import scipy.stats as ss
 import scipy.special as scipy_special
-from nose.tools import assert_raises
 from mxnet import np, npx
 from mxnet.base import MXNetError
 from mxnet.test_utils import assert_almost_equal, use_np, set_default_context
@@ -109,7 +108,3 @@ def test_np_fallback_decorator():
     # does not support functions with no return values
     assertRaises(ValueError, empty_ret_func)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_operator_gpu.py b/tests/python/gpu/test_operator_gpu.py
index a017b8c..798f861 100644
--- a/tests/python/gpu/test_operator_gpu.py
+++ b/tests/python/gpu/test_operator_gpu.py
@@ -23,14 +23,14 @@ import multiprocessing as mp
 import mxnet as mx
 import numpy as np
 import unittest
-from nose.tools import assert_raises
+import pytest
 from mxnet.test_utils import check_consistency, set_default_context, assert_almost_equal, assert_allclose
 from mxnet.base import MXNetError
 from mxnet import autograd
 
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown, assert_raises_cudnn_not_satisfied, assert_raises_cuda_not_satisfied
+from common import setup_module, with_seed, teardown_module, assert_raises_cudnn_not_satisfied, assert_raises_cuda_not_satisfied
 from common import run_in_spawned_process
 from test_operator import *
 from test_numpy_ndarray import *
@@ -46,7 +46,6 @@ from test_ndarray import *
 from test_subgraph_op import *
 from test_gluon_gpu import _test_bulking
 from test_contrib_operator import test_multibox_target_op
-from test_tvm_op import *
 from test_contrib_optimizer import test_adamw
 
 set_default_context(mx.gpu(0))
@@ -1342,7 +1341,7 @@ def test_bilinear_resize_op():
     check_consistency(sym, ctx_list)
 
     sym = mx.sym.contrib.BilinearResize2D(data, height=10, width=5, align_corners=False)
-    check_consistency(sym, ctx_list)    
+    check_consistency(sym, ctx_list)
 
     sym = mx.sym.contrib.BilinearResize2D(data, None, scale_height=2, scale_width=0.5, mode='odd_scale', align_corners=True)
     check_consistency(sym, ctx_list)
@@ -2274,7 +2273,7 @@ def test_kernel_error_checking():
 
 def test_incorrect_gpu():
     # Try setting dev_id to a really big number
-    assert_raises(MXNetError, mx.nd.ones, (2,2), ctx=mx.gpu(100001))
+    pytest.raises(MXNetError, mx.nd.ones, (2,2), ctx=mx.gpu(100001))
 
 @with_seed()
 def test_batchnorm_backwards_notrain():
@@ -2526,7 +2525,7 @@ def run_math(op, shape, dtype="float32", check_value=True):
 def test_math():
     ops = ['log', 'erf', 'square']
     check_value= True
-    shape_lst = [[1000], [100,1000], [10,100,100], [10,100,100,100]] 
+    shape_lst = [[1000], [100,1000], [10,100,100], [10,100,100,100]]
     dtypes = ["float32", "float64"]
     for shape in shape_lst:
         for dtype in dtypes:
@@ -2548,6 +2547,3 @@ def test_arange_like_dtype():
         for v in out:
             assert v.dtype == t
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_predictor.py b/tests/python/gpu/test_predictor.py
index 4838a76..592733a 100644
--- a/tests/python/gpu/test_predictor.py
+++ b/tests/python/gpu/test_predictor.py
@@ -31,7 +31,7 @@ from mxnet.test_utils import assert_almost_equal, download_model
 from mxnet.contrib.amp import amp
 from mxnet.base import NDArrayHandle, py_str
 sys.path.insert(0, os.path.join(curr_path, '../unittest'))
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 @with_seed()
 def test_predictor_with_dtype():
@@ -122,7 +122,3 @@ def test_predictor_amp():
                                                                          cast_optional_params=True)
     compare_module_cpredict(result_sym, result_arg_params, result_aux_params, monitor_callback=True)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/gpu/test_tvm_bridge.py b/tests/python/gpu/test_tvm_bridge.py
index 5c87536..7a4339c 100644
--- a/tests/python/gpu/test_tvm_bridge.py
+++ b/tests/python/gpu/test_tvm_bridge.py
@@ -61,7 +61,3 @@ def test_tvm_bridge():
                       "float32", "float64"]:
             check(tgt, dtype)
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/quantization_gpu/test_quantization_gpu.py b/tests/python/gpu/test_tvm_op_gpu.py
similarity index 85%
copy from tests/python/quantization_gpu/test_quantization_gpu.py
copy to tests/python/gpu/test_tvm_op_gpu.py
index 4f2d70e..fbb16bf 100644
--- a/tests/python/quantization_gpu/test_quantization_gpu.py
+++ b/tests/python/gpu/test_tvm_op_gpu.py
@@ -14,19 +14,14 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+
+import mxnet as mx
+from mxnet.test_utils import set_default_context
 import os
 import sys
-import mxnet as mx
-
-
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
-sys.path.insert(0, os.path.join(curr_path, '../quantization'))
-from mxnet.test_utils import set_default_context
-from test_quantization import *
+sys.path.insert(0, os.path.join(curr_path, '../unittest'))
+from common import setup_module, teardown_module
+from test_tvm_op import *
 
 set_default_context(mx.gpu(0))
-
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/mkl/test_bf16_operator.py b/tests/python/mkl/test_bf16_operator.py
index b275c96..888b5d2 100644
--- a/tests/python/mkl/test_bf16_operator.py
+++ b/tests/python/mkl/test_bf16_operator.py
@@ -25,7 +25,6 @@ import collections
 import ctypes
 import itertools
 import mxnet.contrib.amp as amp
-from nose.tools import assert_raises
 from mxnet.test_utils import set_default_context, download_model, same_symbol_structure, assert_almost_equal_with_err, rand_shape_nd
 from mxnet.gluon.model_zoo.vision import get_model
 from mxnet.gluon import SymbolBlock, nn, rnn
@@ -55,7 +54,7 @@ def check_operator_accuracy(sym_fp32, sym_bf16, data_shape, num_input_data=1, bf
         the relative threshold
     atol: float
         the absolute threshold
-    etol: float 
+    etol: float
         The error rate threshold, allow a small amount of value not consistent between bf16 and fp32
     """
     if not isinstance(data_shape, tuple):
@@ -105,7 +104,7 @@ def check_operator_accuracy(sym_fp32, sym_bf16, data_shape, num_input_data=1, bf
             exe_bf16.arg_dict[arg_name][:] = arg_params_fp32[arg_name]
         else:
             exe_bf16.arg_dict[arg_name][:] = mx.nd.amp_cast(arg_params_fp32[arg_name], dtype=bfloat16)
-    
+
     for aux_name in aux_names:
         if bf16_use_fp32_params:
             exe_bf16.aux_dict[aux_name][:] = aux_params_fp32[aux_name]
@@ -169,7 +168,7 @@ def test_bf16_pooling():
     pool_conventions = ["full", "valid"]
     for new_params in itertools.product(data_shapes, pool_types, pool_conventions):
         pool_params.update({"pool_type": new_params[1], "pooling_convention": new_params[2]})
-        
+
         data_sym_fp32 = mx.sym.Variable(name='data')
         data_sym_bf16 = mx.sym.Variable(name='data', dtype=bfloat16)
         pool_fp32 = mx.sym.Pooling(data_sym_fp32, **pool_params)
@@ -230,7 +229,7 @@ def test_bf16_abs():
         data_sym_bf16 = mx.sym.Variable(name='data', dtype=bfloat16)
         sym_fp32 = mx.sym.abs(data_sym_fp32)
         sym_bf16 = mx.sym.abs(data_sym_bf16)
-        
+
         check_operator_accuracy(sym_fp32, sym_bf16, data_shape, bf16_use_fp32_params=True)
 
 @with_seed()
@@ -285,6 +284,3 @@ def test_bf16_fallback():
     conv_bf16 = mx.sym.Convolution(data_sym_bf16, **conv_params)
     check_operator_accuracy(sym_fp32=conv_fp32, sym_bf16=conv_bf16, data_shape=(3, 32, 28, 28, 4), bf16_use_fp32_params=False)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/mkl/test_contrib_amp.py b/tests/python/mkl/test_contrib_amp.py
index 5d57740..ec88851 100644
--- a/tests/python/mkl/test_contrib_amp.py
+++ b/tests/python/mkl/test_contrib_amp.py
@@ -24,7 +24,7 @@ import warnings
 import collections
 import ctypes
 import mxnet.contrib.amp as amp
-from nose.tools import assert_raises
+import pytest
 from mxnet.test_utils import set_default_context, download_model, same_symbol_structure, assert_almost_equal
 from mxnet.gluon.model_zoo.vision import get_model
 from mxnet.gluon import SymbolBlock, nn, rnn
@@ -117,18 +117,18 @@ def test_amp_conversion():
             "convert_symbol generating wrong computation graph"
 
         # convert_symbol called with incorrect inputs
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="bfloat16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["elemwise_add"])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="bfloat16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["Activation"],
                       conditional_fp32_ops=[('Activation', 'act_type', ['selu'])])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="bfloat16", target_dtype_ops=["Activation"],
                       fp32_ops=["Activation"],
                       conditional_fp32_ops=[('Activation', 'act_type', ['selu'])])
-        assert_raises(AssertionError, amp.convert_symbol, res,
+        pytest.raises(AssertionError, amp.convert_symbol, res,
                       target_dtype="bfloat16", target_dtype_ops=["FullyConnected"],
                       fp32_ops=["FullyConnected"])
 
@@ -495,7 +495,3 @@ def test_bf16_casting():
     exe = final_res.simple_bind(ctx=mx.cpu(), data=(1, 2), data2=(1, 2))
     assert exe.arg_arrays[0].dtype == bfloat16
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/mkl/test_mkldnn.py b/tests/python/mkl/test_mkldnn.py
index 731a761..82519a1 100644
--- a/tests/python/mkl/test_mkldnn.py
+++ b/tests/python/mkl/test_mkldnn.py
@@ -702,6 +702,3 @@ def test_elemwise_add():
     for stype in stypes:
         check_elemwise_add_training(stype)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/mkl/test_quantization_mkldnn.py b/tests/python/mkl/test_quantization_mkldnn.py
index 8ba2f2b..f243217 100644
--- a/tests/python/mkl/test_quantization_mkldnn.py
+++ b/tests/python/mkl/test_quantization_mkldnn.py
@@ -25,7 +25,7 @@ sys.path.insert(0, os.path.join(curr_path, '../quantization'))
 from test_quantization import *
 
 if __name__ == '__main__':
-    import nose
-    nose.runmodule()
+    import pytest
+    pytest.main()
     del os.environ['ENABLE_MKLDNN_QUANTIZATION_TEST']
     del os.environ['MXNET_SUBGRAPH_BACKEND']
diff --git a/tests/python/mkl/test_subgraph.py b/tests/python/mkl/test_subgraph.py
index 65b73e4..862fd90 100644
--- a/tests/python/mkl/test_subgraph.py
+++ b/tests/python/mkl/test_subgraph.py
@@ -999,7 +999,3 @@ def test_quantized_fc_bias_overflow():
     helper_quantized_fc_bias_overflow(-1e-6, +1e-6, -1e-6, +1e-6)
     helper_quantized_fc_bias_overflow(0, 0, 0, 0)
 
-
-if __name__ == "__main__":
-  import nose
-  nose.runmodule()
diff --git a/tests/python/profiling/test_nvtx.py b/tests/python/profiling/test_nvtx.py
index 507b438..a80e33e 100644
--- a/tests/python/profiling/test_nvtx.py
+++ b/tests/python/profiling/test_nvtx.py
@@ -46,7 +46,3 @@ def test_nvtx_ranges_present_in_profile():
     # Verify that we have some expected output from the engine.
     assert "Range \"WaitForVar\"" in profiler_output
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/quantization/test_quantization.py b/tests/python/quantization/test_quantization.py
index 4e42a5d..2572cab 100644
--- a/tests/python/quantization/test_quantization.py
+++ b/tests/python/quantization/test_quantization.py
@@ -1257,7 +1257,3 @@ def test_get_optimal_thresholds():
         assert 'layer1' in th_dict
         assert_almost_equal(np.array([th_dict['layer1'][1]]), expected_threshold, rtol=1e-2, atol=1e-4)
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/quantization_gpu/test_quantization_gpu.py b/tests/python/quantization_gpu/test_quantization_gpu.py
index 4f2d70e..0f14fa1 100644
--- a/tests/python/quantization_gpu/test_quantization_gpu.py
+++ b/tests/python/quantization_gpu/test_quantization_gpu.py
@@ -25,8 +25,3 @@ from mxnet.test_utils import set_default_context
 from test_quantization import *
 
 set_default_context(mx.gpu(0))
-
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/tensorrt/lenet5_train.py b/tests/python/tensorrt/lenet5_train.py
old mode 100755
new mode 100644
diff --git a/tests/python/tensorrt/test_cvnets.py b/tests/python/tensorrt/test_cvnets.py
index 4b8eb48..99312d7 100644
--- a/tests/python/tensorrt/test_cvnets.py
+++ b/tests/python/tensorrt/test_cvnets.py
@@ -167,8 +167,3 @@ def test_tensorrt_on_cifar_resnets(batch_size=32, tolerance=0.1, num_workers=1):
     finally:
         mx.contrib.tensorrt.set_use_fp16(original_use_fp16)
 
-
-if __name__ == '__main__':
-    import nose
-
-    nose.runmodule()
diff --git a/tests/python/tensorrt/test_ops.py b/tests/python/tensorrt/test_ops.py
index af1c453..dfbbb8e 100644
--- a/tests/python/tensorrt/test_ops.py
+++ b/tests/python/tensorrt/test_ops.py
@@ -512,6 +512,3 @@ def test_dropout():
             sym = mx.sym.Dropout(data, p=0.7, mode=mode, axes=(0,))
             check_unsupported_single_sym(sym)
 
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/tensorrt/test_resnet18.py b/tests/python/tensorrt/test_resnet18.py
index 9fd99ab..e146423 100644
--- a/tests/python/tensorrt/test_resnet18.py
+++ b/tests/python/tensorrt/test_resnet18.py
@@ -69,6 +69,3 @@ def test_tensorrt_resnet18_feature_vect():
     finally:
         mx.contrib.tensorrt.set_use_fp16(original_precision_value)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/tensorrt/test_tensorrt_lenet5.py b/tests/python/tensorrt/test_tensorrt_lenet5.py
index 78f41ca..a34b634 100644
--- a/tests/python/tensorrt/test_tensorrt_lenet5.py
+++ b/tests/python/tensorrt/test_tensorrt_lenet5.py
@@ -116,6 +116,3 @@ def test_tensorrt_inference():
         """Absolute diff. between MXNet & TensorRT accuracy (%f) exceeds threshold (%f):
            MXNet = %f, TensorRT = %f""" % (absolute_accuracy_diff, epsilon, mx_pct, trt_pct)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/train/test_conv.py b/tests/python/train/test_conv.py
old mode 100644
new mode 100755
diff --git a/tests/python/unittest/common.py b/tests/python/unittest/common.py
index ab2d191..eac0265 100644
--- a/tests/python/unittest/common.py
+++ b/tests/python/unittest/common.py
@@ -16,7 +16,7 @@
 # under the License.
 
 from __future__ import print_function
-import sys, os, logging
+import sys, os, logging, functools
 import multiprocessing as mp
 import mxnet as mx
 import numpy as np
@@ -29,7 +29,7 @@ sys.path.insert(0, os.path.join(curr_path, '../../../python'))
 
 import models
 from contextlib import contextmanager
-from nose.tools import make_decorator, assert_raises
+import pytest
 import tempfile
 
 def assertRaises(expected_exception, func, *args, **kwargs):
@@ -43,7 +43,7 @@ def assertRaises(expected_exception, func, *args, **kwargs):
 
 
 def default_logger():
-    """A logger used to output seed information to nosetests logs."""
+    """A logger used to output seed information to logs."""
     logger = logging.getLogger(__name__)
     # getLogger() lookups will return the same logger, but only add the handler once.
     if not len(logger.handlers):
@@ -109,7 +109,7 @@ def _assert_raise_cuxx_version_not_satisfied(min_version, cfg):
         left = version_left.split(".")
         right = version_right.split(".")
 
-        # 0 pad shortest version - e.g. 
+        # 0 pad shortest version - e.g.
         # less_than("9.1", "9.1.9") == less_than("9.1.0", "9.1.9")
         longest = max(len(left), len(right))
         left.extend([0] * (longest - len(left)))
@@ -123,7 +123,7 @@ def _assert_raise_cuxx_version_not_satisfied(min_version, cfg):
         return False
 
     def test_helper(orig_test):
-        @make_decorator(orig_test)
+        @functools.wraps(orig_test)
         def test_new(*args, **kwargs):
             cuxx_off = os.getenv(cfg['TEST_OFF_ENV_VAR']) == 'true'
             cuxx_env_version = os.getenv(cfg['VERSION_ENV_VAR'], None if cuxx_off else cfg['DEFAULT_VERSION'])
@@ -131,7 +131,7 @@ def _assert_raise_cuxx_version_not_satisfied(min_version, cfg):
             if not cuxx_test_disabled or mx.context.current_context().device_type == 'cpu':
                 orig_test(*args, **kwargs)
             else:
-                assert_raises((MXNetError, RuntimeError), orig_test, *args, **kwargs)
+                pytest.raises((MXNetError, RuntimeError), orig_test, *args, **kwargs)
         return test_new
     return test_helper
 
@@ -154,7 +154,7 @@ def assert_raises_cuda_not_satisfied(min_version):
 
 def with_seed(seed=None):
     """
-    A decorator for nosetests test functions that manages rng seeds.
+    A decorator for test functions that manages rng seeds.
 
     Parameters
     ----------
@@ -181,13 +181,13 @@ def with_seed(seed=None):
     can then set the environment variable MXNET_TEST_SEED to
     the value reported, then rerun the test with:
 
-        nosetests --verbose -s <test_module_name.py>:<failing_test>
+        pytest --verbose --capture=no <test_module_name.py>::<failing_test>
 
     To run a test repeatedly, set MXNET_TEST_COUNT=<NNN> in the environment.
-    To see the seeds of even the passing tests, add '--logging-level=DEBUG' to nosetests.
+    To see the seeds of even the passing tests, add '--log-level=DEBUG' to pytest.
     """
     def test_helper(orig_test):
-        @make_decorator(orig_test)
+        @functools.wraps(orig_test)
         def test_new(*args, **kwargs):
             test_count = int(os.getenv('MXNET_TEST_COUNT', '1'))
             env_seed_str = os.getenv('MXNET_TEST_SEED')
@@ -206,7 +206,7 @@ def with_seed(seed=None):
                 mx.random.seed(this_test_seed)
                 random.seed(this_test_seed)
                 logger = default_logger()
-                # 'nosetests --logging-level=DEBUG' shows this msg even with an ensuing core dump.
+                # 'pytest --logging-level=DEBUG' shows this msg even with an ensuing core dump.
                 test_count_msg = '{} of {}: '.format(i+1,test_count) if test_count > 1 else ''
                 test_msg = ('{}Setting test np/mx/python random seeds, use MXNET_TEST_SEED={}'
                             ' to reproduce.').format(test_count_msg, this_test_seed)
@@ -226,12 +226,12 @@ def with_seed(seed=None):
 
 def setup_module():
     """
-    A function with a 'magic name' executed automatically before each nosetests module
+    A function with a 'magic name' executed automatically before each pytest module
     (file of tests) that helps reproduce a test segfault by setting and outputting the rng seeds.
 
     The segfault-debug procedure on a module called test_module.py is:
 
-    1. run "nosetests --verbose test_module.py".  A seg-faulting output might be:
+    1. run "pytest --verbose test_module.py".  A seg-faulting output might be:
 
        [INFO] np, mx and python random seeds = 4018804151
        test_module.test1 ... ok
@@ -239,7 +239,7 @@ def setup_module():
 
     2. Copy the module-starting seed into the next command, then run:
 
-       MXNET_MODULE_SEED=4018804151 nosetests --logging-level=DEBUG --verbose test_module.py
+       MXNET_MODULE_SEED=4018804151 pytest --logging-level=DEBUG --verbose test_module.py
 
        Output might be:
 
@@ -251,7 +251,7 @@ def setup_module():
        Illegal instruction (core dumped)
 
     3. Copy the segfaulting-test seed into the command:
-       MXNET_TEST_SEED=1435005594 nosetests --logging-level=DEBUG --verbose test_module.py:test2
+       MXNET_TEST_SEED=1435005594 pytest --logging-level=DEBUG --verbose test_module.py:test2
        Output might be:
 
        [INFO] np, mx and python random seeds = 2481884723
@@ -301,9 +301,9 @@ except:  # Python 2 support
         def __exit__(self, exc_type, exc_value, traceback):
             shutil.rmtree(self._dirname)
 
-def teardown():
+def teardown_module():
     """
-    A function with a 'magic name' executed automatically after each nosetests test module.
+    A function with a 'magic name' executed automatically after each pytest test module.
 
     It waits for all operations in one file to finish before carrying on the next.
     """
@@ -316,7 +316,7 @@ def with_post_test_cleanup():
     Required especially by large tensor tests that have memory footprints in GBs.
     """
     def test_helper(orig_test):
-        @make_decorator(orig_test)
+        @functools.wraps(orig_test)
         def test_new(*args, **kwargs):
             logger = default_logger()
             try:
@@ -373,4 +373,25 @@ def run_in_spawned_process(func, env, *args):
         finally:
             os.environ.clear()
             os.environ.update(orig_environ)
-    return True
\ No newline at end of file
+    return True
+
+
+def retry(n):
+    """Retry n times before failing for stochastic test cases."""
+    # TODO(szha): replace with flaky
+    # https://github.com/apache/incubator-mxnet/issues/17803
+    assert n > 0
+    def test_helper(orig_test):
+        @functools.wraps(orig_test)
+        def test_new(*args, **kwargs):
+            """Wrapper for tests function."""
+            for _ in range(n):
+                try:
+                    orig_test(*args, **kwargs)
+                except AssertionError as e:
+                    err = e
+                    continue
+                return
+            raise err
+        return test_new
+    return test_helper
diff --git a/tests/python-pytest/onnx/README.md b/tests/python/unittest/onnx/README.md
similarity index 100%
rename from tests/python-pytest/onnx/README.md
rename to tests/python/unittest/onnx/README.md
diff --git a/tests/python-pytest/onnx/backend.py b/tests/python/unittest/onnx/backend.py
similarity index 100%
rename from tests/python-pytest/onnx/backend.py
rename to tests/python/unittest/onnx/backend.py
diff --git a/tests/python-pytest/onnx/backend_rep.py b/tests/python/unittest/onnx/backend_rep.py
similarity index 100%
rename from tests/python-pytest/onnx/backend_rep.py
rename to tests/python/unittest/onnx/backend_rep.py
diff --git a/tests/python-pytest/onnx/backend_test.py b/tests/python/unittest/onnx/backend_test.py
old mode 100755
new mode 100644
similarity index 95%
rename from tests/python-pytest/onnx/backend_test.py
rename to tests/python/unittest/onnx/backend_test.py
index 5e8e198..69d9e14
--- a/tests/python-pytest/onnx/backend_test.py
+++ b/tests/python/unittest/onnx/backend_test.py
@@ -35,7 +35,7 @@ backends = ['mxnet', 'gluon']
 pytest_plugins = "onnx.backend.test.report",
 
 
-def test_suite(backend_tests):  # type: () -> unittest.TestSuite
+def build_test_suite(backend_tests):  # type: () -> unittest.TestSuite
     '''
     TestSuite that can be run by TestRunner
     This has been borrowed from onnx/onnx/backend/test/runner/__init__.py,
@@ -89,4 +89,4 @@ for bkend in backends:
         log.info('Executing tests for ' + bkend + ' backend: ' + operation)
         mxnet_backend.MXNetBackend.set_params(bkend, operation)
         BACKEND_TESTS = prepare_tests(mxnet_backend, operation)
-        unittest.TextTestRunner().run(test_suite(BACKEND_TESTS.enable_report()))
+        unittest.TextTestRunner().run(build_test_suite(BACKEND_TESTS.enable_report()))
diff --git a/tests/python-pytest/onnx/mxnet_export_test.py b/tests/python/unittest/onnx/mxnet_export_test.py
similarity index 90%
rename from tests/python-pytest/onnx/mxnet_export_test.py
rename to tests/python/unittest/onnx/mxnet_export_test.py
index 90e92cc..117a2cf 100644
--- a/tests/python-pytest/onnx/mxnet_export_test.py
+++ b/tests/python/unittest/onnx/mxnet_export_test.py
@@ -18,11 +18,15 @@
 
 # pylint: disable=too-many-locals,wrong-import-position,import-error
 from __future__ import absolute_import
-import os
+import os, sys
 import unittest
 import logging
 import tempfile
+curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
+sys.path.insert(0, os.path.join(curr_path, '..'))
+from common import setup_module, teardown_module, with_seed
 from mxnet import nd, sym
+from mxnet.test_utils import set_default_context
 from mxnet.gluon import nn
 from mxnet.contrib import onnx as onnx_mxnet
 import mxnet as mx
@@ -30,7 +34,6 @@ import mxnet as mx
 logger = logging.getLogger()
 logger.setLevel(logging.DEBUG)
 
-
 def _assert_sym_equal(lhs, rhs):
     assert lhs.list_inputs() == rhs.list_inputs()  # input names must be identical
     assert len(lhs.list_outputs()) == len(rhs.list_outputs())  # number of outputs must be identical
@@ -74,19 +77,24 @@ def _check_onnx_export(net, group_outputs=False, shape_type=tuple, extra_params=
         # Confirm network outputs are the same
         imported_net_output = _force_list(imported_net(data))
         for out, imp_out in zip(output, imported_net_output):
-            mx.test_utils.assert_almost_equal(out, imp_out)
+            mx.test_utils.assert_almost_equal(out, imp_out, atol=1e-5, rtol=1e-5)
 
 
 class TestExport(unittest.TestCase):
     """ Tests ONNX export.
     """
 
+    def setUp(self):
+        set_default_context(mx.cpu(0))
+
+    @with_seed()
     def test_onnx_export_single_output(self):
         net = nn.HybridSequential(prefix='single_output_net')
         with net.name_scope():
             net.add(nn.Dense(100, activation='relu'), nn.Dense(10))
         _check_onnx_export(net)
 
+    @with_seed()
     def test_onnx_export_multi_output(self):
         class MultiOutputBlock(nn.HybridBlock):
             def __init__(self):
@@ -104,18 +112,17 @@ class TestExport(unittest.TestCase):
         assert len(sym.Group(net(sym.Variable('data'))).list_outputs()) == 10
         _check_onnx_export(net, group_outputs=True)
 
+    @with_seed()
     def test_onnx_export_list_shape(self):
         net = nn.HybridSequential(prefix='list_shape_net')
         with net.name_scope():
             net.add(nn.Dense(100, activation='relu'), nn.Dense(10))
         _check_onnx_export(net, shape_type=list)
 
+    @with_seed()
     def test_onnx_export_extra_params(self):
         net = nn.HybridSequential(prefix='extra_params_net')
         with net.name_scope():
             net.add(nn.Dense(100, activation='relu'), nn.Dense(10))
         _check_onnx_export(net, extra_params={'extra_param': nd.array([1, 2])})
 
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/python-pytest/onnx/test_cases.py b/tests/python/unittest/onnx/test_cases.py
similarity index 100%
rename from tests/python-pytest/onnx/test_cases.py
rename to tests/python/unittest/onnx/test_cases.py
diff --git a/tests/python-pytest/onnx/test_models.py b/tests/python/unittest/onnx/test_models.py
similarity index 57%
rename from tests/python-pytest/onnx/test_models.py
rename to tests/python/unittest/onnx/test_models.py
index f857861..ce54a34 100644
--- a/tests/python-pytest/onnx/test_models.py
+++ b/tests/python/unittest/onnx/test_models.py
@@ -20,7 +20,7 @@
 from __future__ import absolute_import
 import sys
 import os
-import unittest
+import pytest
 import logging
 import tarfile
 from collections import namedtuple
@@ -101,78 +101,64 @@ def forward_pass(sym, arg, aux, data_names, input_data):
     return mod.get_outputs()[0].asnumpy()
 
 
-class TestModel(unittest.TestCase):
-    """ Tests for models.
-    Tests are dynamically added.
-    Therefore edit test_models to add more tests.
-    """
-    def test_import_export(self):
-        def get_model_results(modelpath):
-            symbol, args, aux = onnx_mxnet.import_model(modelpath)
-
-            data = onnx_mxnet.get_model_metadata(modelpath)
-            data_names = [input_name[0] for input_name in data.get('input_tensor_data')]
-
-            result = []
-            for input_data, output_data in zip(inputs, outputs):
-                output = forward_pass(symbol, args, aux, data_names, input_data)
-                result.append(output)
-            return symbol, args, aux, result, data
-
-        for test in test_cases:
-            model_name, input_shape, output_shape = test
-            with self.subTest(model_name):
-                model_path, inputs, outputs = get_test_files(model_name)
-                logging.info("Translating " + model_name + " from ONNX model zoo to MXNet")
-
-                sym, arg_params, aux_params, expected_result, _ = get_model_results(model_path)
-
-                params = {}
-                params.update(arg_params)
-                params.update(aux_params)
-
-                dir_path = os.path.dirname(model_path)
-                new_model_name = "exported_" + model_name + ".onnx"
-                onnx_file = os.path.join(dir_path, new_model_name)
-
-                logging.info("Translating converted model from mxnet to ONNX")
-                converted_model_path = onnx_mxnet.export_model(sym, params, [input_shape], np.float32, onnx_file)
-
-                sym, arg_params, aux_params, actual_result, metadata = get_model_results(converted_model_path)
-
-                assert len(metadata) == 2
-                assert metadata.get('input_tensor_data')
-                assert metadata.get('input_tensor_data')[0][1] == input_shape
-                assert metadata.get('output_tensor_data')
-                assert metadata.get('output_tensor_data')[0][1] == output_shape
-
-                # verify the results
-                for expected, actual in zip(expected_result, actual_result):
-                    npt.assert_equal(expected.shape, actual.shape)
-                    npt.assert_almost_equal(expected, actual, decimal=3)
-
-                logging.info(model_name + " conversion successful")
-
-    def test_nodims_import(self):
-        # Download test model without dims mentioned in params
-        test_model = download(test_model_path, dirname=CURR_PATH.__str__())
-        input_data = np.array([0.2, 0.5])
-        nd_data = mx.nd.array(input_data).expand_dims(0)
-        sym, arg_params, aux_params = onnx_mxnet.import_model(test_model)
-        model_metadata = onnx_mxnet.get_model_metadata(test_model)
-        input_names = [inputs[0] for inputs in model_metadata.get('input_tensor_data')]
-        output_data = forward_pass(sym, arg_params, aux_params, input_names, nd_data)
-        assert(output_data.shape == (1,1))
-
-# test_case = ("model name", input shape, output shape)
-test_cases = [
+@pytest.mark.parametrize('model_name,input_shape,output_shape', [
     ("bvlc_googlenet", (1, 3, 224, 224), (1, 1000)),
     ("bvlc_reference_caffenet", (1, 3, 224, 224), (1, 1000)),
     ("bvlc_reference_rcnn_ilsvrc13", (1, 3, 224, 224), (1, 200)),
     ("inception_v1", (1, 3, 224, 224), (1, 1000)),
     ("inception_v2", (1, 3, 224, 224), (1, 1000))
-]
-
-
-if __name__ == '__main__':
-    unittest.main()
+])
+def test_import_export(model_name, input_shape, output_shape):
+    def get_model_results(modelpath):
+        symbol, args, aux = onnx_mxnet.import_model(modelpath)
+
+        data = onnx_mxnet.get_model_metadata(modelpath)
+        data_names = [input_name[0] for input_name in data.get('input_tensor_data')]
+
+        result = []
+        for input_data, output_data in zip(inputs, outputs):
+            output = forward_pass(symbol, args, aux, data_names, input_data)
+            result.append(output)
+        return symbol, args, aux, result, data
+
+    model_path, inputs, outputs = get_test_files(model_name)
+    logging.info("Translating " + model_name + " from ONNX model zoo to MXNet")
+
+    sym, arg_params, aux_params, expected_result, _ = get_model_results(model_path)
+
+    params = {}
+    params.update(arg_params)
+    params.update(aux_params)
+
+    dir_path = os.path.dirname(model_path)
+    new_model_name = "exported_" + model_name + ".onnx"
+    onnx_file = os.path.join(dir_path, new_model_name)
+
+    logging.info("Translating converted model from mxnet to ONNX")
+    converted_model_path = onnx_mxnet.export_model(sym, params, [input_shape], np.float32, onnx_file)
+
+    sym, arg_params, aux_params, actual_result, metadata = get_model_results(converted_model_path)
+
+    assert len(metadata) == 2
+    assert metadata.get('input_tensor_data')
+    assert metadata.get('input_tensor_data')[0][1] == input_shape
+    assert metadata.get('output_tensor_data')
+    assert metadata.get('output_tensor_data')[0][1] == output_shape
+
+    # verify the results
+    for expected, actual in zip(expected_result, actual_result):
+        npt.assert_equal(expected.shape, actual.shape)
+        npt.assert_almost_equal(expected, actual, decimal=3)
+
+    logging.info(model_name + " conversion successful")
+
+def test_nodims_import():
+    # Download test model without dims mentioned in params
+    test_model = download(test_model_path, dirname=CURR_PATH.__str__())
+    input_data = np.array([0.2, 0.5])
+    nd_data = mx.nd.array(input_data).expand_dims(0)
+    sym, arg_params, aux_params = onnx_mxnet.import_model(test_model)
+    model_metadata = onnx_mxnet.get_model_metadata(test_model)
+    input_names = [inputs[0] for inputs in model_metadata.get('input_tensor_data')]
+    output_data = forward_pass(sym, arg_params, aux_params, input_names, nd_data)
+    assert(output_data.shape == (1,1))
diff --git a/tests/python-pytest/onnx/test_node.py b/tests/python/unittest/onnx/test_node.py
similarity index 94%
rename from tests/python-pytest/onnx/test_node.py
rename to tests/python/unittest/onnx/test_node.py
index 9604551..f7fc5c8 100644
--- a/tests/python-pytest/onnx/test_node.py
+++ b/tests/python/unittest/onnx/test_node.py
@@ -161,7 +161,7 @@ class TestNode(unittest.TestCase):
                     onnx_attrs = _fix_attributes(attrs, fix_attrs)
                     onnxmodel = get_onnx_graph(test_name, names, input_tensors, onnx_name, outputshape, onnx_attrs)
 
-                bkd_rep = backend.prepare(onnxmodel, operation='export')
+                bkd_rep = backend.prepare(onnxmodel, operation='export', backend='mxnet')
                 output = bkd_rep.run(inputs)
 
                 if check_value:
@@ -195,16 +195,17 @@ class TestNode(unittest.TestCase):
         npt.assert_almost_equal(result, forward_op)
 
     def test_imports(self):
-        for test in import_test_cases:
-            test_name, onnx_name, inputs, np_op, attrs = test
-            with self.subTest(test_name):
-                names, input_tensors, inputsym = get_input_tensors(inputs)
-                np_out = [np_op(*inputs, **attrs)]
-                output_shape = np.shape(np_out)
-                onnx_model = get_onnx_graph(test_name, names, input_tensors, onnx_name, output_shape, attrs)
-                bkd_rep = backend.prepare(onnx_model, operation='import')
-                mxnet_out = bkd_rep.run(inputs)
-                npt.assert_almost_equal(np_out, mxnet_out, decimal=4)
+        for bk in ['mxnet', 'gluon']:
+            for test in import_test_cases:
+                test_name, onnx_name, inputs, np_op, attrs = test
+                with self.subTest(test_name):
+                    names, input_tensors, inputsym = get_input_tensors(inputs)
+                    np_out = [np_op(*inputs, **attrs)]
+                    output_shape = np.shape(np_out)
+                    onnx_model = get_onnx_graph(test_name, names, input_tensors, onnx_name, output_shape, attrs)
+                    bkd_rep = backend.prepare(onnx_model, operation='import', backend=bk)
+                    mxnet_out = bkd_rep.run(inputs)
+                    npt.assert_almost_equal(np_out, mxnet_out, decimal=4)
 
     def test_exports(self):
         input_shape = (2,1,3,1)
diff --git a/tests/python/unittest/test_autograd.py b/tests/python/unittest/test_autograd.py
index 61955f0..69b61b4 100644
--- a/tests/python/unittest/test_autograd.py
+++ b/tests/python/unittest/test_autograd.py
@@ -20,7 +20,7 @@ import mxnet.ndarray as nd
 from mxnet.ndarray import zeros_like
 from mxnet.autograd import *
 from mxnet.test_utils import *
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.test_utils import EnvManager
 
 
@@ -467,7 +467,3 @@ def test_gradient():
     dx.backward()
     assert abs(x.grad.asscalar() - 2.71828175) < 1e-7
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_base.py b/tests/python/unittest/test_base.py
index 3189729..07d4295 100644
--- a/tests/python/unittest/test_base.py
+++ b/tests/python/unittest/test_base.py
@@ -17,7 +17,6 @@
 
 import mxnet as mx
 from mxnet.base import data_dir
-from nose.tools import *
 import os
 import unittest
 import logging
@@ -47,4 +46,3 @@ class MXNetDataDirTest(unittest.TestCase):
         del os.environ['MXNET_HOME']
         self.assertEqual(data_dir(), prev_data_dir)
 
-
diff --git a/tests/python/unittest/test_contrib_autograd.py b/tests/python/unittest/test_contrib_autograd.py
index 1c878e3..c376eb7 100644
--- a/tests/python/unittest/test_contrib_autograd.py
+++ b/tests/python/unittest/test_contrib_autograd.py
@@ -18,7 +18,7 @@
 import mxnet.ndarray as nd
 from mxnet.contrib.autograd import *
 from mxnet.test_utils import *
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 def autograd_assert(*args, **kwargs):
     func   = kwargs["func"]
@@ -190,7 +190,3 @@ def test_retain_grad():
     raise AssertionError(
         "differentiating the same graph twice without retain_graph should fail")
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_control_flow.py b/tests/python/unittest/test_contrib_control_flow.py
index a93c109..b703402 100644
--- a/tests/python/unittest/test_contrib_control_flow.py
+++ b/tests/python/unittest/test_contrib_control_flow.py
@@ -1151,9 +1151,9 @@ def test_cond():
                     ]
                 )
 
-class TestRNNLayer(gluon.HybridBlock):
+class RNNLayer(gluon.HybridBlock):
     def __init__(self, cell_type, hidden_size, prefix=None, params=None):
-        super(TestRNNLayer, self).__init__(prefix=prefix, params=params)
+        super(RNNLayer, self).__init__(prefix=prefix, params=params)
         self.cell = cell_type(hidden_size, prefix='rnn_')
 
     def hybrid_forward(self, F, inputs, states):
@@ -1166,7 +1166,7 @@ def check_contrib_rnn(cell_type, num_states):
     rnn_data = mx.nd.normal(loc=0, scale=1, shape=(5, batch_size, 50))
     state_shape = (batch_size, hidden_size)
     states = [mx.nd.normal(loc=0, scale=1, shape=state_shape) for i in range(num_states)]
-    layer = TestRNNLayer(cell_type, hidden_size)
+    layer = RNNLayer(cell_type, hidden_size)
     layer.initialize(ctx=default_context())
     res1 = layer(rnn_data, states)
     params1 = layer.collect_params()
@@ -1184,7 +1184,7 @@ def check_contrib_rnn(cell_type, num_states):
             {'static_alloc': True},
             {'static_alloc': True, 'static_shape': True} ]
     for config in configs:
-        layer = TestRNNLayer(cell_type, hidden_size)
+        layer = RNNLayer(cell_type, hidden_size)
         layer.initialize(ctx=default_context())
         layer.hybridize(**config)
         res2 = layer(rnn_data, states)
@@ -2168,6 +2168,3 @@ def test_foreach_with_unkown_dim():
     _, output_shape, _ = outs.infer_shape_partial()
     assert_allclose((0, 3, 32, 32), output_shape[0])
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_hawkesll.py b/tests/python/unittest/test_contrib_hawkesll.py
index a4b1d9d..8f02737 100644
--- a/tests/python/unittest/test_contrib_hawkesll.py
+++ b/tests/python/unittest/test_contrib_hawkesll.py
@@ -154,8 +154,3 @@ def test_hawkesll_backward_single_mark():
 
     assert np.allclose(beta.grad.asnumpy().sum(), -0.05371582)
 
-
-if __name__ == "__main__":
-    import nose
-
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_operator.py b/tests/python/unittest/test_contrib_operator.py
index 476dfac..717ce7f 100644
--- a/tests/python/unittest/test_contrib_operator.py
+++ b/tests/python/unittest/test_contrib_operator.py
@@ -444,7 +444,3 @@ def test_modulated_deformable_convolution():
                         else:
                             rtol, atol = 0.05, 1e-3
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_optimizer.py b/tests/python/unittest/test_contrib_optimizer.py
index 5f7c51f..aae7672 100644
--- a/tests/python/unittest/test_contrib_optimizer.py
+++ b/tests/python/unittest/test_contrib_optimizer.py
@@ -196,6 +196,3 @@ def test_adamw():
     for nElem in range(6):
         run_adamw_test(nElem+1)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_stes_op.py b/tests/python/unittest/test_contrib_stes_op.py
index 5864ec9..163ab4a 100644
--- a/tests/python/unittest/test_contrib_stes_op.py
+++ b/tests/python/unittest/test_contrib_stes_op.py
@@ -132,6 +132,3 @@ def test_contrib_sign_ste():
     check_ste(net_type_str="SignSTENET", w_init=w_init, hybridize=True, in_data=in_data)
     check_ste(net_type_str="SignSTENET", w_init=w_init, hybridize=False, in_data=in_data)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
\ No newline at end of file
diff --git a/tests/python/unittest/test_contrib_svrg_module.py b/tests/python/unittest/test_contrib_svrg_module.py
index 79407d1..f135255 100644
--- a/tests/python/unittest/test_contrib_svrg_module.py
+++ b/tests/python/unittest/test_contrib_svrg_module.py
@@ -307,7 +307,3 @@ def test_fit():
     estimated_mse = 1e-5
     assert metric.get()[1] < estimated_mse
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_svrg_optimizer.py b/tests/python/unittest/test_contrib_svrg_optimizer.py
index f7d90d1..cb6fdcf 100644
--- a/tests/python/unittest/test_contrib_svrg_optimizer.py
+++ b/tests/python/unittest/test_contrib_svrg_optimizer.py
@@ -95,7 +95,3 @@ def test_kvstore_init_aux_keys():
     # updated with AssignmentOptimizer
     assert same(param_weight_full_init.asnumpy(), np.array([2, 2, 2]))
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_contrib_text.py b/tests/python/unittest/test_contrib_text.py
index 4072cc8..0778e5a 100644
--- a/tests/python/unittest/test_contrib_text.py
+++ b/tests/python/unittest/test_contrib_text.py
@@ -792,7 +792,3 @@ def test_get_and_pretrain_file_names():
 
     assertRaises(KeyError, text.embedding.get_pretrained_file_names, 'unknown$$')
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_deferred_compute.py b/tests/python/unittest/test_deferred_compute.py
index cebb690..6d250d5 100644
--- a/tests/python/unittest/test_deferred_compute.py
+++ b/tests/python/unittest/test_deferred_compute.py
@@ -19,11 +19,11 @@ import functools
 import operator
 
 import numpy as np
-from nose.tools import raises
 
 import mxnet as mx
 import mxnet._deferred_compute as dc
 from mxnet.base import MXNetError
+import pytest
 
 
 def _all_same(arrays1, arrays2, message=''):
@@ -213,7 +213,7 @@ def test_dc_subset_of_output():
     _all_assert_dc(_dc_simple_setup, f)
 
 
-@raises(MXNetError)  # Should raise NotImplementedError https://github.com/apache/incubator-mxnet/issues/17522
+@pytest.mark.xfail(raises=MXNetError)  # Should raise NotImplementedError https://github.com/apache/incubator-mxnet/issues/17522
 def test_dc_inplace():
     def f(a, *, nd):
         a[:5] = 0
@@ -245,7 +245,7 @@ def test_dc_get_symbol_called_twice():
     assert sym2.list_inputs() == ['a']
 
 
-@raises(MXNetError)  # Should raise ValueError https://github.com/apache/incubator-mxnet/issues/17522
+@pytest.mark.xfail(raises=MXNetError)  # Should raise ValueError https://github.com/apache/incubator-mxnet/issues/17522
 def test_dc_set_variable_called_twice():
     a = mx.np.arange(10)
     dc.set_variable(a, mx.sym.var('a'))
@@ -342,7 +342,7 @@ def test_dc_simple_boolean_indexing():
         _assert_dc(setup, f, mode=mode)
 
 
-@raises(TypeError)  # Advanced indexing
+@pytest.mark.xfail(raises=TypeError)  # Advanced indexing
 def test_dc_list_indexing():
     def f(a, *, nd):
         assert nd is mx.np
@@ -352,7 +352,7 @@ def test_dc_list_indexing():
         _assert_dc(_dc_simple_setup, f, mode=mode)
 
 
-@raises(TypeError)  # Advanced indexing
+@pytest.mark.xfail(raises=TypeError)  # Advanced indexing
 def test_dc_numpy_indexing():
     def f(a, *, nd):
         assert nd is mx.np
@@ -430,7 +430,7 @@ def test_dc_hybridblock():
         _assert_dc_gluon(_dc_gluon_simple_setup, net, numpy=True)
 
 
-@raises(RuntimeError)
+@pytest.mark.xfail(raises=RuntimeError)
 def test_dc_hybridblock_deferred_init_no_infer_shape():
     class MyBlock(mx.gluon.HybridBlock):
         def __init__(self, *, prefix=None, params=None):
@@ -496,7 +496,7 @@ def test_dc_hybridblock_dynamic_shape():
         _assert_dc_gluon(setup, net, numpy=True)
 
 
-@raises(RuntimeError)
+@pytest.mark.xfail(raises=RuntimeError)
 def test_dc_hybridblock_symbolblock():
     model = mx.gluon.nn.HybridSequential()
     model.add(mx.gluon.nn.Dense(128, activation='tanh'))
@@ -530,7 +530,3 @@ def test_dc_hybridblock_symbolblock():
 
     _all_same([out], [out_hybrid])
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_dgl_graph.py b/tests/python/unittest/test_dgl_graph.py
index 805adc2..89533fb 100644
--- a/tests/python/unittest/test_dgl_graph.py
+++ b/tests/python/unittest/test_dgl_graph.py
@@ -240,6 +240,3 @@ def test_adjacency():
     assert_array_equal(adj.indices, g.indices)
     assert_array_equal(adj.data, mx.nd.ones(shape=g.indices.shape))
 
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_dlpack.py b/tests/python/unittest/test_dlpack.py
index fb64f8d..46bdde7 100644
--- a/tests/python/unittest/test_dlpack.py
+++ b/tests/python/unittest/test_dlpack.py
@@ -43,6 +43,3 @@ def test_from_dlpack_backward_compatibility():
     z = from_dlpack_old(y)
     assert_almost_equal(x.asnumpy(), z.asnumpy(), rtol=1e-5, atol=1e-5)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_dynamic_shape.py b/tests/python/unittest/test_dynamic_shape.py
index 1b043c7..b61fbee 100644
--- a/tests/python/unittest/test_dynamic_shape.py
+++ b/tests/python/unittest/test_dynamic_shape.py
@@ -48,7 +48,3 @@ def test_dynamic_shape():
     assert_almost_equal(result.asnumpy(), result_nd)
     assert_almost_equal(data.grad.asnumpy(), data_grad_nd)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_engine.py b/tests/python/unittest/test_engine.py
index 61d94dd..fafc675 100644
--- a/tests/python/unittest/test_engine.py
+++ b/tests/python/unittest/test_engine.py
@@ -15,7 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import nose
 import mxnet as mx
 import os
 import unittest
@@ -70,8 +69,3 @@ def test_engine_openmp_after_fork():
             print("Child omp max threads: {}".format(omp_max_threads))
             assert omp_max_threads == 1
 
-
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_engine_import.py b/tests/python/unittest/test_engine_import.py
index 303f3ce..7675cf8 100644
--- a/tests/python/unittest/test_engine_import.py
+++ b/tests/python/unittest/test_engine_import.py
@@ -25,7 +25,7 @@ except NameError:  # Python 3
 
 def test_engine_import():
     import mxnet
-        
+
     engine_types = ['', 'NaiveEngine', 'ThreadedEngine', 'ThreadedEnginePerDevice']
 
     for type in engine_types:
@@ -35,7 +35,3 @@ def test_engine_import():
             os.environ.pop('MXNET_ENGINE_TYPE', None)
         reload(mxnet)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_exc_handling.py b/tests/python/unittest/test_exc_handling.py
index e3c3337..8657bec 100644
--- a/tests/python/unittest/test_exc_handling.py
+++ b/tests/python/unittest/test_exc_handling.py
@@ -18,11 +18,11 @@
 import mxnet as mx
 import numpy as np
 from mxnet import gluon
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.gluon import nn
 from mxnet.base import MXNetError
 from mxnet.test_utils import assert_exception, default_context, set_default_context, use_np
-from nose.tools import assert_raises
+import pytest
 
 
 @with_seed()
@@ -35,7 +35,7 @@ def test_exc_imperative():
             c.asnumpy()
 
     imperative(exec_numpy=False)
-    assert_raises(MXNetError, imperative, exec_numpy=True)
+    pytest.raises(MXNetError, imperative, exec_numpy=True)
 
 @with_seed()
 def test_exc_symbolic():
@@ -69,11 +69,11 @@ def test_exc_symbolic():
             else:
                 outputs[0].asnumpy()
 
-    assert_raises(MXNetError, symbolic, exec_backward=False)
-    assert_raises(MXNetError, symbolic, exec_backward=True)
+    pytest.raises(MXNetError, symbolic, exec_backward=False)
+    pytest.raises(MXNetError, symbolic, exec_backward=True)
 
-    assert_raises(MXNetError, symbolic, exec_backward=False, waitall=True)
-    assert_raises(MXNetError, symbolic, exec_backward=True, waitall=True)
+    pytest.raises(MXNetError, symbolic, exec_backward=False, waitall=True)
+    pytest.raises(MXNetError, symbolic, exec_backward=True, waitall=True)
 
 @with_seed()
 def test_exc_gluon():
@@ -93,9 +93,9 @@ def test_exc_gluon():
             z.wait_to_read()
 
     gluon(exec_wait=False)
-    assert_raises(MXNetError, gluon, exec_wait=True)
+    pytest.raises(MXNetError, gluon, exec_wait=True)
 
-    assert_raises(MXNetError, gluon, waitall=True)
+    pytest.raises(MXNetError, gluon, waitall=True)
 
 @with_seed()
 def test_exc_multiple_waits():
@@ -152,8 +152,8 @@ def test_exc_mutable_var_fail():
             mx.nd.waitall()
         else:
             a.asnumpy()
-    assert_raises(MXNetError, mutable_var_check, waitall=False)
-    assert_raises(MXNetError, mutable_var_check, waitall=True)
+    pytest.raises(MXNetError, mutable_var_check, waitall=False)
+    pytest.raises(MXNetError, mutable_var_check, waitall=True)
 
 @with_seed()
 def test_multiple_waitalls():
@@ -189,16 +189,16 @@ def test_opencv_exception():
         img = mx.nd.ones((1200, 1600, 3))
         img = mx.image.imresize(img, 320, 320, interp=-1)
         img.asnumpy()
-    assert_raises(MXNetError, check_resize)
+    pytest.raises(MXNetError, check_resize)
 
 
 @with_seed()
 def test_np_reshape_exception():
     a = mx.np.ones((10, 10))
     a.reshape((-1,)).asnumpy()  # Check no-raise
-    assert_raises(MXNetError, lambda: a.reshape((1,)))
-    assert_raises(MXNetError, lambda: mx.np.reshape(a, (1,)))
-    assert_raises(MXNetError, lambda: mx.np.reshape(a, (-1, 3)))
+    pytest.raises(MXNetError, lambda: a.reshape((1,)))
+    pytest.raises(MXNetError, lambda: mx.np.reshape(a, (1,)))
+    pytest.raises(MXNetError, lambda: mx.np.reshape(a, (-1, 3)))
 
 
 @with_seed()
@@ -208,10 +208,6 @@ def test_np_random_incorrect_named_arguments():
     for op_name in random_ops:
         op = getattr(mx.np.random, op_name, None)
         assert op is not None
-        assert_raises(TypeError, op, shape=())
-        assert_raises(TypeError, op, shape=None)
+        pytest.raises(TypeError, op, shape=())
+        pytest.raises(TypeError, op, shape=None)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_executor.py b/tests/python/unittest/test_executor.py
index 2bc696f..300e4b2 100644
--- a/tests/python/unittest/test_executor.py
+++ b/tests/python/unittest/test_executor.py
@@ -17,7 +17,7 @@
 
 import numpy as np
 import mxnet as mx
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.test_utils import assert_almost_equal
 
 
@@ -164,7 +164,3 @@ def test_reshape():
     # weight ndarray is shared between exe and new_exe
     assert np.all(new_exe.arg_arrays[1].asnumpy() == 1)
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py
index 6dda662..c9a99b5 100644
--- a/tests/python/unittest/test_gluon.py
+++ b/tests/python/unittest/test_gluon.py
@@ -27,11 +27,11 @@ from mxnet.util import is_np_array
 from mxnet.ndarray.ndarray import _STORAGE_TYPE_STR_TO_ID
 from mxnet.test_utils import use_np
 import mxnet.numpy as _mx_np
-from common import (setup_module, with_seed, assertRaises, teardown,
+from common import (setup_module, with_seed, assertRaises, teardown_module,
                     assert_raises_cudnn_not_satisfied)
 import numpy as np
 from numpy.testing import assert_array_equal
-from nose.tools import raises, assert_raises
+import pytest
 from copy import deepcopy
 import warnings
 import json
@@ -54,12 +54,12 @@ def test_parameter():
     assert p.list_ctx() == [mx.cpu(1), mx.cpu(2)]
 
 @with_seed()
-@raises(AssertionError)
+@pytest.mark.xfail(raises=AssertionError)
 def test_invalid_parameter_stype():
     p = gluon.Parameter('weight', shape=(10, 10), stype='invalid')
 
 @with_seed()
-@raises(AssertionError)
+@pytest.mark.xfail(raises=AssertionError)
 def test_invalid_parameter_grad_stype():
     p = gluon.Parameter('weight', shape=(10, 10), grad_stype='invalid')
 
@@ -424,7 +424,7 @@ def test_symbol_block():
     assert np.dtype(prediction.dtype) == np.dtype(np.float32)
 
 @with_seed()
-@raises(AssertionError)
+@pytest.mark.xfail(raises=AssertionError)
 def test_sparse_symbol_block():
     data = mx.sym.var('data')
     weight = mx.sym.var('weight', stype='row_sparse')
@@ -434,7 +434,7 @@ def test_sparse_symbol_block():
     net = gluon.SymbolBlock(out, data)
 
 @with_seed()
-@raises(RuntimeError)
+@pytest.mark.xfail(raises=RuntimeError)
 def test_sparse_hybrid_block():
     params = gluon.ParameterDict('net_')
     params.get('weight', shape=(5,5), stype='row_sparse', dtype='float32')
@@ -501,17 +501,17 @@ def test_hybrid_block_none_args():
         foo = FooNested()
         if do_hybridize:
             foo.hybridize()
-        assert_raises(ValueError, foo, None, None)
+        pytest.raises(ValueError, foo, None, None)
 
     # Make sure the ValueError is correctly raised
     foo = FooNested()
     foo.hybridize()
     foo(None, mx.nd.ones((10,)))  # Pass for the first time to initialize the cached op
-    assert_raises(ValueError, lambda: foo(mx.nd.ones((10,)), mx.nd.ones((10,))))
+    pytest.raises(ValueError, lambda: foo(mx.nd.ones((10,)), mx.nd.ones((10,))))
     foo = FooNested()
-    assert_raises(ValueError, lambda: foo(mx.nd.ones((10,)), mx.sym.var('a')))
+    pytest.raises(ValueError, lambda: foo(mx.nd.ones((10,)), mx.sym.var('a')))
     foo = FooNested()
-    assert_raises(ValueError, lambda: foo(mx.sym.var('a'), mx.nd.ones((10,))))
+    pytest.raises(ValueError, lambda: foo(mx.sym.var('a'), mx.nd.ones((10,))))
 
     # Test the case of the default values
     foo1 = FooDefault()
@@ -529,7 +529,7 @@ def test_hybrid_block_none_args():
     out1 = foo1(mx.nd.ones((10,)), None)
     out2 = foo1(mx.nd.ones((10,)))
     assert_almost_equal(out1.asnumpy(), out2.asnumpy())
-    assert_raises(ValueError, lambda: foo1(mx.nd.ones((10,)), mx.nd.ones((10,))))
+    pytest.raises(ValueError, lambda: foo1(mx.nd.ones((10,)), mx.nd.ones((10,))))
 
 
 @with_seed()
@@ -567,13 +567,13 @@ def test_hybrid_block_hybrid_no_hybrid():
     # 4. Allow mixing of cpu_pinned and cpu
     foo_hybrid = FooHybrid()
     foo_hybrid.hybridize()
-    assert_raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,)), 1))
+    pytest.raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,)), 1))
     foo_hybrid = FooHybrid()
     foo_hybrid.hybridize()
-    assert_raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,)), mx.sym.var('a')))
+    pytest.raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,)), mx.sym.var('a')))
     foo_hybrid = FooHybrid()
     foo_hybrid.hybridize()
-    assert_raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,), ctx=mx.cpu(1)),
+    pytest.raises(ValueError, lambda: foo_hybrid(mx.nd.ones((10,), ctx=mx.cpu(1)),
                                                  mx.nd.ones((10,), ctx=mx.cpu(2))))
 
 
@@ -900,7 +900,7 @@ def test_layernorm():
         layer.initialize()
         if hybridize:
             layer.hybridize()
-        assert_raises(MXNetError, lambda: layer(mx.nd.ones((2, 11))))
+        pytest.raises(MXNetError, lambda: layer(mx.nd.ones((2, 11))))
 
 @with_seed()
 def test_groupnorm():
@@ -1023,7 +1023,7 @@ def test_block_attr_hidden():
     b.a = 1
 
 
-@raises(TypeError)
+@pytest.mark.xfail(raises=TypeError)
 @with_seed()
 def test_block_attr_block():
     b = gluon.Block()
@@ -1033,7 +1033,7 @@ def test_block_attr_block():
     b.b = (2,)
 
 
-@raises(TypeError)
+@pytest.mark.xfail(raises=TypeError)
 @with_seed()
 def test_block_attr_param():
     b = gluon.Block()
@@ -1886,7 +1886,7 @@ def test_summary():
     net3.summary(mx.nd.ones((80, 32, 5)), begin_state)
 
     net.hybridize()
-    assert_raises(AssertionError, net.summary, mx.nd.ones((32, 3, 224, 224)))
+    pytest.raises(AssertionError, net.summary, mx.nd.ones((32, 3, 224, 224)))
 
 
 @with_seed()
@@ -3215,6 +3215,3 @@ def test_reqs_switching_training_inference():
 
     mx.test_utils.assert_almost_equal(grad1, grad2)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_batch_processor.py b/tests/python/unittest/test_gluon_batch_processor.py
index 8604713..952ed1c 100644
--- a/tests/python/unittest/test_gluon_batch_processor.py
+++ b/tests/python/unittest/test_gluon_batch_processor.py
@@ -27,7 +27,7 @@ from mxnet.gluon import nn
 from mxnet.gluon.contrib.estimator import *
 from mxnet.gluon.contrib.estimator.event_handler import *
 from mxnet.gluon.contrib.estimator.batch_processor import BatchProcessor
-from nose.tools import assert_raises
+import pytest
 
 def _get_test_network():
     net = nn.Sequential()
@@ -66,12 +66,12 @@ def test_batch_processor_fit():
     est.fit(train_data=dataloader,
             epochs=num_epochs)
 
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=dataiter,
                 epochs=num_epochs)
 
     # Input NDArray
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=[mx.nd.ones(shape=(10, 3))],
                 epochs=num_epochs)
 
@@ -105,12 +105,12 @@ def test_batch_processor_validation():
     val_metrics = est.val_metrics
     validation_handler = ValidationHandler(val_data=dataloader, eval_fn=est.evaluate)
 
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=dataiter,
                 val_data=dataiter,
                 epochs=num_epochs)
     # Input NDArray
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=[mx.nd.ones(shape=(10, 3))],
                 val_data=[mx.nd.ones(shape=(10, 3))],
                 epochs=num_epochs)
diff --git a/tests/python/unittest/test_gluon_contrib.py b/tests/python/unittest/test_gluon_contrib.py
index 0ed0d4e..a69b023 100644
--- a/tests/python/unittest/test_gluon_contrib.py
+++ b/tests/python/unittest/test_gluon_contrib.py
@@ -25,7 +25,7 @@ from mxnet.gluon.contrib.nn import (
     Concurrent, HybridConcurrent, Identity, SparseEmbedding, PixelShuffle1D,
     PixelShuffle2D, PixelShuffle3D)
 from mxnet.test_utils import almost_equal, default_context, assert_almost_equal, assert_allclose
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 import numpy as np
 
 
@@ -314,9 +314,9 @@ def test_sampler():
     assert list(interval_sampler) == [0, 3, 6, 9]
 
 
-class TestRNNLayer(gluon.HybridBlock):
+class RNNLayer(gluon.HybridBlock):
     def __init__(self, cell_type, hidden_size, layout, prefix=None, params=None):
-        super(TestRNNLayer, self).__init__(prefix=prefix, params=params)
+        super(RNNLayer, self).__init__(prefix=prefix, params=params)
         self.cell = cell_type(hidden_size, prefix='rnn_')
         self.layout = layout
 
@@ -370,7 +370,7 @@ def check_unroll(cell_type, num_states, layout):
     if valid_length is None:
         valid_length = []
     for config in configs:
-        layer = TestRNNLayer(cell_type, hidden_size, layout)
+        layer = RNNLayer(cell_type, hidden_size, layout)
         layer.initialize(ctx=default_context())
         config(layer)
         res2, states2 = layer(rnn_data, states, valid_length)
@@ -436,7 +436,3 @@ def test_ModulatedDeformableConvolution():
     with mx.autograd.record():
         y = net(x)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_data.py b/tests/python/unittest/test_gluon_data.py
index 29197bd..e6e3cae 100644
--- a/tests/python/unittest/test_gluon_data.py
+++ b/tests/python/unittest/test_gluon_data.py
@@ -23,7 +23,7 @@ import numpy as np
 import random
 from mxnet import gluon
 import platform
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.gluon.data import DataLoader
 import mxnet.ndarray as nd
 from mxnet import context
@@ -382,7 +382,3 @@ def test_dataloader_scope():
 
     assert item is not None
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_data_vision.py b/tests/python/unittest/test_gluon_data_vision.py
index b53dbf0..d810f32 100644
--- a/tests/python/unittest/test_gluon_data_vision.py
+++ b/tests/python/unittest/test_gluon_data_vision.py
@@ -25,7 +25,7 @@ from mxnet.base import MXNetError
 from mxnet.gluon.data.vision import transforms
 from mxnet import image
 from mxnet.test_utils import *
-from common import assertRaises, setup_module, with_seed, teardown
+from common import assertRaises, setup_module, with_seed, teardown_module
 
 import numpy as np
 
@@ -42,12 +42,12 @@ def test_to_tensor():
     out_nd = transforms.ToTensor()(nd.array(data_in, dtype='uint8'))
     assert_almost_equal(out_nd.asnumpy(), np.transpose(
                         data_in.astype(dtype=np.float32) / 255.0, (0, 3, 1, 2)))
-    
+
     # Invalid Input
     invalid_data_in = nd.random.uniform(0, 255, (5, 5, 300, 300, 3)).astype(dtype=np.uint8)
     transformer = transforms.ToTensor()
     assertRaises(MXNetError, transformer, invalid_data_in)
-    
+
     # Bounds (0->0, 255->1)
     data_in = np.zeros((10, 20, 3)).astype(dtype=np.uint8)
     out_nd = transforms.ToTensor()(nd.array(data_in, dtype='uint8'))
@@ -126,7 +126,7 @@ def test_resize():
         assertRaises(MXNetError, invalid_transform, data_in)
 
     for dtype in ['uint8', 'float32', 'float64']:
-        _test_resize_with_diff_type(dtype)    
+        _test_resize_with_diff_type(dtype)
 
 
 @with_seed()
@@ -159,7 +159,7 @@ def test_crop_resize():
         # test with resize height and width should be greater than 0
         transformer = transforms.CropResize(0, 0, 100, 50, (-25, 25), 1)
         assertRaises(MXNetError, transformer, data_in)
-        # test height and width should be greater than 0 
+        # test height and width should be greater than 0
         transformer = transforms.CropResize(0, 0, -100, -50)
         assertRaises(MXNetError, transformer, data_in)
         # test cropped area is bigger than input data
@@ -168,7 +168,7 @@ def test_crop_resize():
         assertRaises(MXNetError, transformer, data_bath_in)
 
     for dtype in ['uint8', 'float32', 'float64']:
-        _test_crop_resize_with_diff_type(dtype)  
+        _test_crop_resize_with_diff_type(dtype)
 
     # test nd.image.crop backward
     def test_crop_backward(test_nd_arr, TestCase):
@@ -288,7 +288,7 @@ def test_random_rotation():
 @with_seed()
 def test_random_transforms():
     from mxnet.gluon.data.vision import transforms
-    
+
     tmp_t = transforms.Compose([transforms.Resize(300), transforms.RandomResizedCrop(224)])
     transform = transforms.Compose([transforms.RandomApply(tmp_t, 0.5)])
 
@@ -302,6 +302,3 @@ def test_random_transforms():
     assert_almost_equal(num_apply/float(iteration), 0.5, 0.1)
 
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_estimator.py b/tests/python/unittest/test_gluon_estimator.py
index 2c00b16..e33aa74 100644
--- a/tests/python/unittest/test_gluon_estimator.py
+++ b/tests/python/unittest/test_gluon_estimator.py
@@ -20,13 +20,13 @@
 import sys
 import unittest
 import warnings
+import pytest
 
 import mxnet as mx
 from mxnet import gluon
 from mxnet.gluon import nn
 from mxnet.gluon.contrib.estimator import *
 from mxnet.gluon.contrib.estimator.event_handler import *
-from nose.tools import assert_raises
 
 
 def _get_test_network(params=None):
@@ -70,12 +70,12 @@ def test_fit():
     est.fit(train_data=dataloader,
             epochs=num_epochs)
 
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=dataiter,
                 epochs=num_epochs)
 
     # Input NDArray
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=[mx.nd.ones(shape=(10, 3))],
                 epochs=num_epochs)
 
@@ -107,12 +107,12 @@ def test_validation():
     val_metrics = est.val_metrics
     validation_handler = ValidationHandler(val_data=dataloader, eval_fn=est.evaluate)
 
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=dataiter,
                 val_data=dataiter,
                 epochs=num_epochs)
     # Input NDArray
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=[mx.nd.ones(shape=(10, 3))],
                 val_data=[mx.nd.ones(shape=(10, 3))],
                 epochs=num_epochs)
@@ -180,7 +180,7 @@ def test_trainer():
 
     # input invalid trainer
     trainer = 'sgd'
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est = Estimator(net=net,
                         loss=loss,
                         train_metrics=acc,
@@ -215,7 +215,7 @@ def test_metric():
     est.fit(train_data=train_data,
             epochs=num_epochs)
     # input invalid metric
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est = Estimator(net=net,
                         loss=loss,
                         train_metrics='acc',
@@ -238,7 +238,7 @@ def test_loss():
     net.initialize(ctx=ctx)
     trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.001})
     # input invalid loss
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est = Estimator(net=net,
                         loss='mse',
                         train_metrics=acc,
@@ -264,13 +264,13 @@ def test_context():
                     train_metrics=metrics,
                     context=ctx)
     # input invalid context
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est = Estimator(net=net,
                         loss=loss,
                         train_metrics=metrics,
                         context='cpu')
 
-    with assert_raises(AssertionError):
+    with pytest.raises(AssertionError):
         est = Estimator(net=net,
                         loss=loss,
                         train_metrics=metrics,
@@ -360,7 +360,7 @@ def test_default_handlers():
     # handler with mixed metrics, some handler use metrics prepared by estimator
     # some handler use metrics user prepared
     logging = LoggingHandler(metrics=[mx.metric.RMSE("val acc")])
-    with assert_raises(ValueError):
+    with pytest.raises(ValueError):
         est.fit(train_data=train_data, epochs=num_epochs, event_handlers=[logging])
 
     # test handler order
@@ -394,7 +394,7 @@ def test_val_net():
                     val_loss=val_loss,
                     val_net=val_net)
 
-    with assert_raises(RuntimeError):
+    with pytest.raises(RuntimeError):
         est.fit(train_data=dataloader,
                 val_data=dataloader,
                 epochs=num_epochs)
diff --git a/tests/python/unittest/test_gluon_event_handler.py b/tests/python/unittest/test_gluon_event_handler.py
index c81d291..a07282c 100644
--- a/tests/python/unittest/test_gluon_event_handler.py
+++ b/tests/python/unittest/test_gluon_event_handler.py
@@ -34,7 +34,7 @@ try:
 except ImportError:
     from io import StringIO
 
-class TestAxisArrayDataset(Dataset):
+class AxisArrayDataset(Dataset):
     def __init__(self, * args):
         self._length = len(args[1])
         self._data = []
@@ -50,7 +50,7 @@ class TestAxisArrayDataset(Dataset):
     def __len__(self):
         return self._length
 
-class TestHandler(EpochEnd):
+class Handler(EpochEnd):
     def __init__(self):
         pass
 
@@ -73,7 +73,7 @@ def _get_test_data(in_size=32):
 def _get_batch_axis_test_data(in_size=32):
     data = nd.ones((100, in_size))
     label = nd.zeros((1, in_size))
-    data_arr = TestAxisArrayDataset(data, label)
+    data_arr = AxisArrayDataset(data, label)
     return mx.gluon.data.DataLoader(data_arr, batch_size=8)
 
 def test_checkpoint_handler():
@@ -264,8 +264,8 @@ def test_logging_interval():
     info_len = 0
     for info in log_info_list:
         match = re.match(
-            '(\[Epoch \d+\]\[Batch \d+\]\[Samples \d+\] time\/interval: \d+.\d+s' +
-            ' training accuracy: \d+.\d+)', info)
+            r'(\[Epoch \d+\]\[Batch \d+\]\[Samples \d+\] time\/interval: \d+.\d+s' +
+            r' training accuracy: \d+.\d+)', info)
         if match:
             info_len += 1
 
@@ -287,8 +287,8 @@ def test_logging_interval():
     info_len = 0
     for info in log_info_list:
         match = re.match(
-            '(\[Epoch \d+\]\[Batch \d+\]\[Samples \d+\] time\/interval: \d+.\d+s' +
-            ' training accuracy: \d+.\d+)', info)
+            r'(\[Epoch \d+\]\[Batch \d+\]\[Samples \d+\] time\/interval: \d+.\d+s' +
+            r' training accuracy: \d+.\d+)', info)
         if match:
             info_len += 1
 
@@ -319,7 +319,7 @@ def test_validation_handler():
     est = estimator.Estimator(net, loss=ce_loss, train_metrics=acc)
     val_handler = ValidationHandler(val_data=test_data,
                                     eval_fn=est.evaluate,
-                                    event_handlers=TestHandler())
+                                    event_handlers=Handler())
 
     est.fit(train_data=test_data, val_data=test_data,
             event_handlers=[val_handler], epochs=2)
diff --git a/tests/python/unittest/test_gluon_model_zoo.py b/tests/python/unittest/test_gluon_model_zoo.py
index d53dd40..214f44d 100644
--- a/tests/python/unittest/test_gluon_model_zoo.py
+++ b/tests/python/unittest/test_gluon_model_zoo.py
@@ -19,8 +19,9 @@ from __future__ import print_function
 import mxnet as mx
 from mxnet.gluon.model_zoo.vision import get_model
 import sys
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 import multiprocessing
+import pytest
 
 
 def eprint(*args, **kwargs):
@@ -55,6 +56,7 @@ def parallel_download(model_name):
     print(type(model))
 
 @with_seed()
+@pytest.mark.skip(reason='MXNet is not yet safe for forking. Tracked in #17782.')
 def test_parallel_download():
     processes = []
     name = 'mobilenetv2_0.25'
@@ -66,6 +68,3 @@ def test_parallel_download():
     for p in processes:
         p.join()
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_rnn.py b/tests/python/unittest/test_gluon_rnn.py
index 4be96fc..c057194 100644
--- a/tests/python/unittest/test_gluon_rnn.py
+++ b/tests/python/unittest/test_gluon_rnn.py
@@ -938,7 +938,3 @@ def test_bidirectional_unroll_valid_length():
     _check_bidirectional_unroll_valid_length(1)
     _check_bidirectional_unroll_valid_length(3)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_gluon_trainer.py b/tests/python/unittest/test_gluon_trainer.py
index 350700c..414b15c 100644
--- a/tests/python/unittest/test_gluon_trainer.py
+++ b/tests/python/unittest/test_gluon_trainer.py
@@ -24,7 +24,7 @@ from mxnet.gluon import nn
 from mxnet.test_utils import assert_almost_equal
 from common import setup_module, with_seed, assertRaises
 from copy import deepcopy
-from nose.tools import raises, assert_raises
+import pytest
 
 def dict_equ(a, b):
     assert set(a) == set(b)
@@ -32,7 +32,7 @@ def dict_equ(a, b):
         assert (a[k].asnumpy() == b[k].asnumpy()).all()
 
 @with_seed()
-@raises(RuntimeError)
+@pytest.mark.xfail(raises=RuntimeError)
 def test_multi_trainer():
     x = gluon.Parameter('x', shape=(10,), stype='row_sparse')
     x.initialize()
@@ -78,7 +78,7 @@ def test_trainer_with_teststore():
     # Expect exceptions if update_on_kvstore is set to True,
     # because TestStore does not support that
     invalid_trainer = gluon.Trainer([x], 'sgd', kvstore=kv, update_on_kvstore=True)
-    assert_raises(ValueError, invalid_trainer._init_kvstore)
+    pytest.raises(ValueError, invalid_trainer._init_kvstore)
 
 @with_seed()
 def test_trainer():
@@ -110,8 +110,8 @@ def test_trainer():
         dict_equ(trainer._kvstore._updater.states, states)
         assert trainer._optimizer == trainer._kvstore._updater.optimizer
         # invalid usage of update and allreduce_grads if update_on_kvstore
-        assert_raises(AssertionError, trainer.update, 1)
-        assert_raises(AssertionError, trainer.allreduce_grads)
+        pytest.raises(AssertionError, trainer.update, 1)
+        pytest.raises(AssertionError, trainer.allreduce_grads)
     else:
         for updater in trainer._updaters:
             dict_equ(updater.states, states)
diff --git a/tests/python/unittest/test_gluon_utils.py b/tests/python/unittest/test_gluon_utils.py
index bc816b1..4e37596 100644
--- a/tests/python/unittest/test_gluon_utils.py
+++ b/tests/python/unittest/test_gluon_utils.py
@@ -29,7 +29,7 @@ except ImportError:
     import mock
 import mxnet as mx
 import requests
-from nose.tools import raises
+import pytest
 
 
 class MockResponse(requests.Response):
@@ -40,7 +40,7 @@ class MockResponse(requests.Response):
         self.raw = io.BytesIO(content.encode('utf-8'))
 
 
-@raises(Exception)
+@pytest.mark.xfail(raises=Exception)
 @mock.patch(
     'requests.get', mock.Mock(side_effect=requests.exceptions.ConnectionError))
 def test_download_retries():
diff --git a/tests/python/unittest/test_higher_order_grad.py b/tests/python/unittest/test_higher_order_grad.py
index ad31c34..2835736 100644
--- a/tests/python/unittest/test_higher_order_grad.py
+++ b/tests/python/unittest/test_higher_order_grad.py
@@ -22,8 +22,6 @@ from functools import reduce
 from operator import mul
 import random
 
-from nose.tools import ok_
-
 from common import with_seed
 import mxnet
 from mxnet import nd, autograd, gluon
@@ -648,18 +646,18 @@ def test_dense_backward_flatten():
         w_grad_grad_e = nd.dot(o_y, o_x_grad, transpose_a=True)
         x_grad_e = nd.dot(o_y, w)
         x_grad_grad_e = nd.dot(o_y, o_w_grad)
-        ok_(w_grad.shape == w.shape)
-        ok_(w_grad_grad.shape == w.shape)
-        ok_(x_grad.shape == x.shape)
-        ok_(x_grad_grad.shape == x.shape)
+        assert w_grad.shape == w.shape
+        assert w_grad_grad.shape == w.shape
+        assert x_grad.shape == x.shape
+        assert x_grad_grad.shape == x.shape
         w_grad_check = same(flatten2d_right(w_grad), flatten2d_right(w_grad_e))
         w_grad_grad_check = same(flatten2d_right(w_grad_grad), flatten2d_right(w_grad_grad_e))
         x_grad_check = same(flatten2d_right(x_grad), flatten2d_right(x_grad_e))
         x_grad_grad_check = same(flatten2d_right(x_grad_grad), flatten2d_right(x_grad_grad_e))
-        ok_(x_grad_check)
-        ok_(w_grad_check)
-        ok_(x_grad_grad_check)
-        ok_(w_grad_grad_check)
+        assert x_grad_check
+        assert w_grad_check
+        assert x_grad_grad_check
+        assert w_grad_grad_check
 
 @with_seed()
 def test_dense_backward_no_flatten():
@@ -701,12 +699,8 @@ def test_dense_backward_no_flatten():
         w_grad_grad_check = same(flatten2d_left(w_grad_grad), flatten2d_left(w_grad_grad_e))
         x_grad_check = same(flatten2d_left(x_grad), flatten2d_left(x_grad_e))
         x_grad_grad_check = same(flatten2d_left(x_grad_grad), flatten2d_left(x_grad_grad_e))
-        ok_(x_grad_check)
-        ok_(w_grad_check)
-        ok_(x_grad_grad_check)
-        ok_(w_grad_grad_check)
-
+        assert x_grad_check
+        assert w_grad_check
+        assert x_grad_grad_check
+        assert w_grad_grad_check
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_image.py b/tests/python/unittest/test_image.py
index 033b8e5..d9fe0b7 100644
--- a/tests/python/unittest/test_image.py
+++ b/tests/python/unittest/test_image.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import os
 import mxnet as mx
 import numpy as np
 import scipy.ndimage
@@ -23,8 +24,7 @@ from common import assertRaises, with_seed
 import shutil
 import tempfile
 import unittest
-
-from nose.tools import raises
+import pytest
 
 
 def _get_data(url, dirname):
@@ -111,27 +111,23 @@ def _test_imageiter_last_batch(imageiter_list, assert_data_shape):
 
 class TestImage(unittest.TestCase):
     IMAGES_URL = "http://data.mxnet.io/data/test_images.tar.gz"
-    IMAGES = []
-    IMAGES_DIR = None
-
-    @classmethod
-    def setupClass(cls):
-        cls.IMAGES_DIR = tempfile.mkdtemp()
-        cls.IMAGES = _get_data(cls.IMAGES_URL, cls.IMAGES_DIR)
-        print("Loaded {} images".format(len(cls.IMAGES)))
-
-    @classmethod
-    def teardownClass(cls):
-        if cls.IMAGES_DIR:
-            print("cleanup {}".format(cls.IMAGES_DIR))
-            shutil.rmtree(cls.IMAGES_DIR)
-
-    @raises(mx.base.MXNetError)
+
+    def setUp(self):
+        self.IMAGES_DIR = tempfile.mkdtemp()
+        self.IMAGES = _get_data(self.IMAGES_URL, self.IMAGES_DIR)
+        print("Loaded {} images".format(len(self.IMAGES)))
+
+    def tearDown(self):
+        if self.IMAGES_DIR:
+            print("cleanup {}".format(self.IMAGES_DIR))
+            shutil.rmtree(self.IMAGES_DIR)
+
+    @pytest.mark.xfail(raises=mx.base.MXNetError)
     def test_imread_not_found(self):
         x = mx.img.image.imread("/139810923jadjsajlskd.___adskj/blah.jpg")
 
     def test_imread_vs_imdecode(self):
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             with open(img, 'rb') as fp:
                 str_image = fp.read()
                 image = mx.image.imdecode(str_image, to_rgb=0)
@@ -143,7 +139,7 @@ class TestImage(unittest.TestCase):
             import cv2
         except ImportError:
             raise unittest.SkipTest("Unable to import cv2.")
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             with open(img, 'rb') as fp:
                 str_image = fp.read()
                 image = mx.image.imdecode(str_image, to_rgb=0)
@@ -155,18 +151,18 @@ class TestImage(unittest.TestCase):
             import cv2
         except ImportError:
             return
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             with open(img, 'rb') as fp:
                 str_image = bytearray(fp.read())
                 image = mx.image.imdecode(str_image, to_rgb=0)
             cv_image = cv2.imread(img)
             assert_almost_equal(image.asnumpy(), cv_image)
 
-    @raises(mx.base.MXNetError)
+    @pytest.mark.xfail(raises=mx.base.MXNetError)
     def test_imdecode_empty_buffer(self):
         mx.image.imdecode(b'', to_rgb=0)
 
-    @raises(mx.base.MXNetError)
+    @pytest.mark.xfail(raises=mx.base.MXNetError)
     def test_imdecode_invalid_image(self):
         image = mx.image.imdecode(b'clearly not image content')
         assert_equal(image, None)
@@ -182,7 +178,7 @@ class TestImage(unittest.TestCase):
             import cv2
         except ImportError:
             raise unittest.SkipTest("Unable to import cv2")
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             cv_img = cv2.imread(img)
             mx_img = mx.nd.array(cv_img[:, :, (2, 1, 0)])
             h, w, _ = cv_img.shape
@@ -204,7 +200,7 @@ class TestImage(unittest.TestCase):
             import cv2
         except ImportError:
             raise unittest.SkipTest("Unable to import cv2")
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             cv_img = cv2.imread(img)
             mx_img = mx.nd.array(cv_img[:, :, (2, 1, 0)])
             new_h = np.random.randint(1, 1000)
@@ -229,10 +225,11 @@ class TestImage(unittest.TestCase):
             assert_almost_equal(mx_result.asnumpy(), (src - mean) / std, atol=1e-3)
 
     def test_imageiter(self):
-        im_list = [[np.random.randint(0, 5), x] for x in TestImage.IMAGES]
-        fname = './data/test_imageiter.lst'
+        print(self.IMAGES)
+        im_list = [[np.random.randint(0, 5), x] for x in self.IMAGES]
+        fname = os.path.join(self.IMAGES_DIR, 'test_imageiter.lst')
         file_list = ['\t'.join([str(k), str(np.random.randint(0, 5)), x])
-                        for k, x in enumerate(TestImage.IMAGES)]
+                        for k, x in enumerate(self.IMAGES)]
         with open(fname, 'w') as f:
             for line in file_list:
                 f.write(line + '\n')
@@ -244,15 +241,15 @@ class TestImage(unittest.TestCase):
                 path_imglist = fname if test == 'path_imglist' else None
                 imageiter_list = [
                     mx.image.ImageIter(2, (3, 224, 224), label_width=1, imglist=imglist,
-                        path_imglist=path_imglist, path_root='', dtype=dtype),
+                        path_imglist=path_imglist, path_root=self.IMAGES_DIR, dtype=dtype),
                     mx.image.ImageIter(3, (3, 224, 224), label_width=1, imglist=imglist,
-                        path_imglist=path_imglist, path_root='', dtype=dtype, last_batch_handle='discard'),
+                        path_imglist=path_imglist, path_root=self.IMAGES_DIR, dtype=dtype, last_batch_handle='discard'),
                     mx.image.ImageIter(3, (3, 224, 224), label_width=1, imglist=imglist,
-                        path_imglist=path_imglist, path_root='', dtype=dtype, last_batch_handle='pad'),
+                        path_imglist=path_imglist, path_root=self.IMAGES_DIR, dtype=dtype, last_batch_handle='pad'),
                     mx.image.ImageIter(3, (3, 224, 224), label_width=1, imglist=imglist,
-                        path_imglist=path_imglist, path_root='', dtype=dtype, last_batch_handle='roll_over'),
+                        path_imglist=path_imglist, path_root=self.IMAGES_DIR, dtype=dtype, last_batch_handle='roll_over'),
                     mx.image.ImageIter(3, (3, 224, 224), label_width=1, imglist=imglist, shuffle=True,
-                        path_imglist=path_imglist, path_root='', dtype=dtype, last_batch_handle='pad')
+                        path_imglist=path_imglist, path_root=self.IMAGES_DIR, dtype=dtype, last_batch_handle='pad')
                 ]
                 _test_imageiter_last_batch(imageiter_list, (2, 3, 224, 224))
 
@@ -262,7 +259,7 @@ class TestImage(unittest.TestCase):
             import cv2
         except ImportError:
             raise unittest.SkipTest("Unable to import cv2")
-        for img in TestImage.IMAGES:
+        for img in self.IMAGES:
             cv_img = cv2.imread(img)
             mx_img = mx.nd.array(cv_img)
             top = np.random.randint(1, 10)
@@ -296,34 +293,34 @@ class TestImage(unittest.TestCase):
 
         # only test if all augmenters will work
         # TODO(Joshua Zhang): verify the augmenter outputs
-        im_list = [[0, x] for x in TestImage.IMAGES]
+        im_list = [[0, x] for x in self.IMAGES]
         test_iter = mx.image.ImageIter(2, (3, 224, 224), label_width=1, imglist=im_list,
             resize=640, rand_crop=True, rand_resize=True, rand_mirror=True, mean=True,
             std=np.array([1.1, 1.03, 1.05]), brightness=0.1, contrast=0.1, saturation=0.1,
-            hue=0.1, pca_noise=0.1, rand_gray=0.2, inter_method=10, path_root='', shuffle=True)
+            hue=0.1, pca_noise=0.1, rand_gray=0.2, inter_method=10, path_root=self.IMAGES_DIR, shuffle=True)
         for batch in test_iter:
             pass
 
     def test_image_detiter(self):
-        im_list = [_generate_objects() + [x] for x in TestImage.IMAGES]
-        det_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root='')
+        im_list = [_generate_objects() + [x] for x in self.IMAGES]
+        det_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root=self.IMAGES_DIR)
         for _ in range(3):
             for _ in det_iter:
                 pass
         det_iter.reset()
-        val_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root='')
+        val_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root=self.IMAGES_DIR)
         det_iter = val_iter.sync_label_shape(det_iter)
         assert det_iter.data_shape == val_iter.data_shape
         assert det_iter.label_shape == val_iter.label_shape
 
         # test batch_size is not divisible by number of images
-        det_iter = mx.image.ImageDetIter(4, (3, 300, 300), imglist=im_list, path_root='')
+        det_iter = mx.image.ImageDetIter(4, (3, 300, 300), imglist=im_list, path_root=self.IMAGES_DIR)
         for _ in det_iter:
             pass
 
         # test file list with last batch handle
-        fname = './data/test_imagedetiter.lst'
-        im_list = [[k] + _generate_objects() + [x] for k, x in enumerate(TestImage.IMAGES)]
+        fname = os.path.join(self.IMAGES_DIR, 'test_imagedetiter.lst')
+        im_list = [[k] + _generate_objects() + [x] for k, x in enumerate(self.IMAGES)]
         with open(fname, 'w') as f:
             for line in im_list:
                 line = '\t'.join([str(k) for k in line])
@@ -331,23 +328,23 @@ class TestImage(unittest.TestCase):
 
         imageiter_list = [
             mx.image.ImageDetIter(2, (3, 400, 400),
-                path_imglist=fname, path_root=''),
+                path_imglist=fname, path_root=self.IMAGES_DIR),
             mx.image.ImageDetIter(3, (3, 400, 400),
-                path_imglist=fname, path_root='', last_batch_handle='discard'),
+                path_imglist=fname, path_root=self.IMAGES_DIR, last_batch_handle='discard'),
             mx.image.ImageDetIter(3, (3, 400, 400),
-                path_imglist=fname, path_root='', last_batch_handle='pad'),
+                path_imglist=fname, path_root=self.IMAGES_DIR, last_batch_handle='pad'),
             mx.image.ImageDetIter(3, (3, 400, 400),
-                path_imglist=fname, path_root='', last_batch_handle='roll_over'),
+                path_imglist=fname, path_root=self.IMAGES_DIR, last_batch_handle='roll_over'),
             mx.image.ImageDetIter(3, (3, 400, 400), shuffle=True,
-                path_imglist=fname, path_root='', last_batch_handle='pad')
+                path_imglist=fname, path_root=self.IMAGES_DIR, last_batch_handle='pad')
         ]
         _test_imageiter_last_batch(imageiter_list, (2, 3, 400, 400))
 
     def test_det_augmenters(self):
         # only test if all augmenters will work
         # TODO(Joshua Zhang): verify the augmenter outputs
-        im_list = [_generate_objects() + [x] for x in TestImage.IMAGES]
-        det_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root='',
+        im_list = [_generate_objects() + [x] for x in self.IMAGES]
+        det_iter = mx.image.ImageDetIter(2, (3, 300, 300), imglist=im_list, path_root=self.IMAGES_DIR,
             resize=640, rand_crop=1, rand_pad=1, rand_gray=0.1, rand_mirror=True, mean=True,
             std=np.array([1.1, 1.03, 1.05]), brightness=0.1, contrast=0.1, saturation=0.1,
             pca_noise=0.1, hue=0.1, inter_method=10, min_object_covered=0.5,
@@ -451,7 +448,3 @@ class TestImage(unittest.TestCase):
                                                  angle_limits)
         self.assertEqual(out_batch_image.shape, (3, 3, 30, 60))
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_infer_shape.py b/tests/python/unittest/test_infer_shape.py
index 1312be0..a2f4cfe 100644
--- a/tests/python/unittest/test_infer_shape.py
+++ b/tests/python/unittest/test_infer_shape.py
@@ -18,7 +18,7 @@
 # pylint: skip-file
 import mxnet as mx
 from common import models
-from nose.tools import *
+import pytest
 
 def test_mlp2_infer_shape():
     # Build MLP
@@ -36,7 +36,7 @@ def test_mlp2_infer_shape():
     for k, v in true_shapes.items():
         assert arg_shape_dict[k] == v
 
-@raises(mx.MXNetError)
+@pytest.mark.xfail(raises=mx.MXNetError)
 def test_mlp2_infer_error():
     # Test shape inconsistent case
     out = models.mlp2()
@@ -246,6 +246,3 @@ def test_where_partial_shape():
         _, result, _ = where_op.infer_shape_partial(cond=(-1,), x=(2, 2), y=(2, 2))
         assert result == [None]
 
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_infer_type.py b/tests/python/unittest/test_infer_type.py
index bad83f3..286556a 100644
--- a/tests/python/unittest/test_infer_type.py
+++ b/tests/python/unittest/test_infer_type.py
@@ -20,7 +20,6 @@ import mxnet as mx
 import numpy as np
 from common import models, with_seed
 from mxnet import autograd
-from nose.tools import *
 from mxnet.test_utils import assert_almost_equal
 
 @with_seed()
@@ -52,7 +51,3 @@ def test_infer_multiout_op2():
         test64.backward()
     assert_almost_equal(data64.grad.asnumpy(), data32.grad.asnumpy(), atol=1e-5, rtol=1e-5)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
\ No newline at end of file
diff --git a/tests/python/unittest/test_io.py b/tests/python/unittest/test_io.py
index a13addb..7de8498 100644
--- a/tests/python/unittest/test_io.py
+++ b/tests/python/unittest/test_io.py
@@ -149,7 +149,7 @@ def _test_last_batch_handle(data, labels=None, is_image=False):
         batch_count_list = [40, 39, 39]
     else:
         batch_count_list = [8, 7, 7]
-    
+
     for idx in range(len(last_batch_handle_list)):
         dataiter = mx.io.NDArrayIter(
             data, labels, 128, False, last_batch_handle=last_batch_handle_list[idx])
@@ -168,7 +168,7 @@ def _test_last_batch_handle(data, labels=None, is_image=False):
                        labelcount[int(label[i])] += 1
             else:
                 assert not batch.label, 'label is not empty list'
-            # keep the last batch of 'pad' to be used later 
+            # keep the last batch of 'pad' to be used later
             # to test first batch of roll_over in second iteration
             batch_count += 1
             if last_batch_handle_list[idx] == 'pad' and \
@@ -243,7 +243,7 @@ def test_NDArrayIter_h5py():
     with h5py.File('ndarraytest.h5') as f:
         f.create_dataset('data', data=data)
         f.create_dataset('label', data=labels)
-        
+
         _test_last_batch_handle(f['data'], f['label'])
         _test_last_batch_handle(f['data'], [])
         _test_last_batch_handle(f['data'])
@@ -285,7 +285,7 @@ def test_NDArrayIter_csr():
                      {'data': train_data}, dns, batch_size)
     except ImportError:
         pass
-    
+
     # scipy.sparse.csr_matrix with shuffle
     csr_iter = iter(mx.io.NDArrayIter({'data': train_data}, dns, batch_size,
                                       shuffle=True, last_batch_handle='discard'))
@@ -411,16 +411,15 @@ def test_LibSVMIter():
 
 
 def test_DataBatch():
-    from nose.tools import ok_
     from mxnet.io import DataBatch
     import re
     batch = DataBatch(data=[mx.nd.ones((2, 3))])
-    ok_(re.match(
-        'DataBatch: data shapes: \[\(2L?, 3L?\)\] label shapes: None', str(batch)))
+    assert re.match(
+        r'DataBatch: data shapes: \[\(2L?, 3L?\)\] label shapes: None', str(batch))
     batch = DataBatch(data=[mx.nd.ones((2, 3)), mx.nd.ones(
         (7, 8))], label=[mx.nd.ones((4, 5))])
-    ok_(re.match(
-        'DataBatch: data shapes: \[\(2L?, 3L?\), \(7L?, 8L?\)\] label shapes: \[\(4L?, 5L?\)\]', str(batch)))
+    assert re.match(
+        r'DataBatch: data shapes: \[\(2L?, 3L?\), \(7L?, 8L?\)\] label shapes: \[\(4L?, 5L?\)\]', str(batch))
 
 
 def test_CSVIter():
@@ -462,7 +461,7 @@ def test_ImageRecordIter_seed_augmentation():
         are the equal.
         """
         for batch1, batch2 in zip_longest(dataiter1, dataiter2):
-            
+
             # ensure iterators contain the same number of batches
             # zip_longest will return None if on of the iterators have run out of batches
             assert batch1 and batch2, 'The iterators do not contain the same number of batches'
@@ -533,7 +532,7 @@ def test_ImageRecordIter_seed_augmentation():
         random_h=10,
         max_shear_ratio=2,
         seed_aug=seed_aug)
-    
+
     assert_dataiter_items_equals(dataiter1, dataiter2)
 
     # check whether to get different images after change seed_aug
@@ -573,7 +572,7 @@ def test_ImageRecordIter_seed_augmentation():
         data_shape=(3, 28, 28),
         batch_size=3,
         seed_aug=seed_aug)
-    
+
     assert_dataiter_items_equals(dataiter1, dataiter2)
 
 if __name__ == "__main__":
diff --git a/tests/python/unittest/test_kvstore.py b/tests/python/unittest/test_kvstore.py
index 28d4ec2..ad1fe92 100644
--- a/tests/python/unittest/test_kvstore.py
+++ b/tests/python/unittest/test_kvstore.py
@@ -20,8 +20,9 @@ import mxnet as mx
 import numpy as np
 import unittest
 from mxnet.test_utils import rand_ndarray, assert_almost_equal
-from common import setup_module, with_seed, assertRaises, teardown
+from common import setup_module, with_seed, assertRaises, teardown_module
 from mxnet.base import py_str, MXNetError
+import pytest
 
 shape = (4, 4)
 keys = [5, 7, 11]
@@ -139,6 +140,7 @@ def test_list_kv_pair():
         check_list_kv_pair(init_kv_with_str(), str_keys, stype)
 
 
+@pytest.mark.skip(reason='Skipped due to segfault. Tracked in #18098')
 @with_seed()
 def test_aggregator():
     """aggregate value on muliple devices"""
@@ -175,6 +177,7 @@ def test_aggregator():
 
 
 @with_seed()
+@pytest.mark.skip(reason='Skipped due to segfault. Tracked in #18098')
 def test_sparse_aggregator():
     """aggregate sparse ndarray on muliple devices"""
     def check_sparse_aggregator(sparse_pull):
@@ -344,6 +347,3 @@ def test_invalid_pull():
         check_invalid_key_types_single(kvs[i], single_keys[1 - i])
         check_invalid_key_types_list(kvs[i], list_keys[1 - i])
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_kvstore_custom.py b/tests/python/unittest/test_kvstore_custom.py
index 4f1f309..a8f0869 100644
--- a/tests/python/unittest/test_kvstore_custom.py
+++ b/tests/python/unittest/test_kvstore_custom.py
@@ -20,7 +20,7 @@ import mxnet as mx
 import numpy as np
 import unittest
 from mxnet.test_utils import rand_ndarray, assert_almost_equal
-from common import setup_module, with_seed, assertRaises, teardown
+from common import setup_module, with_seed, assertRaises, teardown_module
 from mxnet.base import py_str, MXNetError
 
 shape = (4, 4)
@@ -190,6 +190,3 @@ def test_set_optimizer():
     kv = mx.kv.create('teststore')
     check_unsupported_methods(kv)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_loss.py b/tests/python/unittest/test_loss.py
index a1a49c9..e779fd6 100644
--- a/tests/python/unittest/test_loss.py
+++ b/tests/python/unittest/test_loss.py
@@ -19,7 +19,7 @@ import mxnet as mx
 import numpy as np
 from mxnet import gluon, autograd
 from mxnet.test_utils import assert_almost_equal, default_context
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 import unittest
 
 
@@ -354,7 +354,7 @@ def test_sdml_loss():
     N = 5 # number of samples
     DIM = 10 # Dimensionality
     EPOCHS = 20
-    
+
     # Generate randomized data and 'positive' samples
     data = mx.random.uniform(-1, 1, shape=(N, DIM))
     pos = data + mx.random.uniform(-0.1, 0.1, shape=(N, DIM)) # correlated paired data
@@ -380,7 +380,7 @@ def test_sdml_loss():
     # After training euclidean distance between aligned pairs should be lower than all non-aligned pairs
     avg_loss = loss.sum()/len(loss)
     assert(avg_loss < 0.05)
-    
+
 @with_seed()
 def test_cosine_loss():
     #Generating samples
@@ -488,7 +488,3 @@ def test_bce_loss_with_pos_weight():
     npy_bce_loss = (- label_npy * np.log(prob_npy)*pos_weight_npy - (1 - label_npy) * np.log(1 - prob_npy)).mean(axis=1)
     assert_almost_equal(mx_bce_loss, npy_bce_loss, rtol=1e-4, atol=1e-5)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_metric.py b/tests/python/unittest/test_metric.py
index e7273fb..d1e1c5a 100644
--- a/tests/python/unittest/test_metric.py
+++ b/tests/python/unittest/test_metric.py
@@ -407,7 +407,3 @@ def test_single_array_input():
     rmse.get()
     _, rmse_res = rmse.get()
     np.testing.assert_almost_equal(rmse_res, 0.1)
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_metric_perf.py b/tests/python/unittest/test_metric_perf.py
index 36cbc68..fc0f8da 100644
--- a/tests/python/unittest/test_metric_perf.py
+++ b/tests/python/unittest/test_metric_perf.py
@@ -118,7 +118,3 @@ def test_metric_performance():
                     run_metric(k, v[1], (data_size * 128)//(n * c), n, c, pred_ctx, label_ctx, **v[0])
                 print("{:-^90}".format(''), file=sys.stderr)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_module.py b/tests/python/unittest/test_module.py
index b829331..65d86f6 100644
--- a/tests/python/unittest/test_module.py
+++ b/tests/python/unittest/test_module.py
@@ -22,7 +22,7 @@ from mxnet.test_utils import *
 import numpy as np
 from functools import reduce
 from mxnet.module.executor_group import DataParallelExecutorGroup
-from common import setup_module, with_seed, assertRaises, teardown
+from common import setup_module, with_seed, assertRaises, teardown_module
 from collections import namedtuple
 curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
 sys.path.insert(0, os.path.join(curr_path, "../train"))
@@ -1029,7 +1029,3 @@ def test_module_init_optimizer():
     mod2.init_optimizer(optimizer=opt)
     assert mod2._optimizer.idx2name == get_module_idx2name(mod2)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_ndarray.py b/tests/python/unittest/test_ndarray.py
index 3a9bd9e..22caf77 100644
--- a/tests/python/unittest/test_ndarray.py
+++ b/tests/python/unittest/test_ndarray.py
@@ -23,7 +23,7 @@ import os
 import pickle as pkl
 import random
 import functools
-from nose.tools import assert_raises, raises
+import pytest
 from common import with_seed, assertRaises, TemporaryDirectory
 from mxnet.test_utils import almost_equal
 from mxnet.test_utils import assert_almost_equal, assert_exception
@@ -1341,7 +1341,7 @@ def test_ndarray_fluent():
     check_fluent_regular('squeeze', {'axis': (1, 3)}, shape=(2, 1, 3, 1, 4))
 
 
-@raises(ValueError)
+@pytest.mark.xfail(raises=ValueError)
 def test_bool_ambiguous():
     bool(mx.nd.ones((2,3,4)))
 
@@ -1603,10 +1603,10 @@ def test_ndarray_indexing():
                   # Test basic indexing with newaxis
                   (None, False),
                   ((1, None, -2, 3, -4), False),
-                  ((1, slice(2, 5), None), False), 
-                  ((slice(None), slice(1, 4), None, slice(2, 3)), False), 
-                  ((slice(1, 3), slice(1, 3), slice(1, 3), slice(1, 3), None), False), 
-                  ((slice(1, 3), slice(1, 3), None, slice(1, 3), slice(1, 3)), False), 
+                  ((1, slice(2, 5), None), False),
+                  ((slice(None), slice(1, 4), None, slice(2, 3)), False),
+                  ((slice(1, 3), slice(1, 3), slice(1, 3), slice(1, 3), None), False),
+                  ((slice(1, 3), slice(1, 3), None, slice(1, 3), slice(1, 3)), False),
                   ((None, slice(1, 2), 3, None), False),
                   ((1, None, 2, 3, None, None, 4), False),
                   # Advanced indexing
@@ -2062,7 +2062,3 @@ def test_load_saved_gpu_array_when_no_gpus_are_present():
     # but there are no GPUs
     array.__setstate__(ndarray_state)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_numpy_gluon.py b/tests/python/unittest/test_numpy_gluon.py
index f3f01fc..7135917 100644
--- a/tests/python/unittest/test_numpy_gluon.py
+++ b/tests/python/unittest/test_numpy_gluon.py
@@ -431,7 +431,3 @@ def test_hybridize_boolean_dtype():
 
     assert mx.test_utils.same(out1.asnumpy(), out2.asnumpy())
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_numpy_interoperability.py b/tests/python/unittest/test_numpy_interoperability.py
index 919e0df..824fa1e 100644
--- a/tests/python/unittest/test_numpy_interoperability.py
+++ b/tests/python/unittest/test_numpy_interoperability.py
@@ -1813,7 +1813,7 @@ def _add_workload_matmul():
             a = np.ones((2,), dtype=dt)
             b = np.ones((2,), dtype=dt)
             OpArgMngr.add_workload('matmul', a, b)
-    
+
     def test_result_types():
         mat = np.ones((1,1))
         vec = np.ones((1,))
@@ -1822,7 +1822,7 @@ def _add_workload_matmul():
             v = vec.astype(dt)
             for arg in [(m, v), (v, m), (m, m)]:
                 OpArgMngr.add_workload('matmul', *arg)
-    
+
     def test_scalar_output():
         vec1 = np.array([2])
         vec2 = np.array([3, 4]).reshape(1, -1)
@@ -1831,7 +1831,7 @@ def _add_workload_matmul():
             v2 = vec2.astype(dt)
             OpArgMngr.add_workload('matmul', v1, v2)
             OpArgMngr.add_workload('matmul', v2.T, v1)
-    
+
     def test_vector_vector_values():
         vec1 = np.array([1, 2])
         vec2 = np.array([3, 4]).reshape(-1, 1)
@@ -1863,7 +1863,7 @@ def _add_workload_matmul():
             m2 = mat2.astype(dt)
             OpArgMngr.add_workload('matmul', m1, v)
             OpArgMngr.add_workload('matmul', m2, v)
-    
+
     def test_matrix_matrix_values():
         mat1 = np.array([[1, 2], [3, 4]])
         mat2 = np.array([[1, 0], [1, 1]])
@@ -3265,7 +3265,3 @@ def test_np_fallback_ops():
     op_list = np.fallback.__all__ + ['linalg.{}'.format(op_name) for op_name in np.fallback_linalg.__all__]
     check_interoperability(op_list)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_numpy_ndarray.py b/tests/python/unittest/test_numpy_ndarray.py
index 0f57947..98267e9 100644
--- a/tests/python/unittest/test_numpy_ndarray.py
+++ b/tests/python/unittest/test_numpy_ndarray.py
@@ -21,12 +21,13 @@ from __future__ import division
 import itertools
 import os
 import unittest
+import pytest
 import numpy as _np
 import mxnet as mx
 from mxnet import np, npx, autograd
 from mxnet.gluon import HybridBlock
-from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndarray, retry, use_np
-from common import with_seed, TemporaryDirectory
+from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndarray, use_np
+from common import with_seed, retry, TemporaryDirectory
 from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, assert_exception, is_op_runnable, collapse_sum_like
 from mxnet.ndarray.ndarray import py_slice
 from mxnet.base import integer_types
@@ -623,7 +624,7 @@ def test_formatting():
     if str(context)[:3] != 'gpu':
         test_0d()
         test_nd_format()
-        test_nd_no_format() 
+        test_nd_no_format()
     # if the program is running in GPU, the formatted string would be appended with context notation
     # for exmpale, if a = np.array([np.pi]), the return value of '{}'.format(a) is '[3.1415927] @gpu(0)'
 
@@ -1238,7 +1239,7 @@ def test_np_ndarray_boolean_indexing():
 
         mx_mask = np.array([[False,True, True],[False, True,False]],dtype=np.bool)
         np_mask = mx_mask.asnumpy()
-        
+
         np_data[0, np_mask] = 5
         mx_data[0, mx_mask] = 5
         assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False)
@@ -1316,7 +1317,3 @@ def test_np_ndarray_pickle():
             a_load = pickle.load(f)
         same(a.asnumpy(), a_load.asnumpy())
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py
index 1edee77..3b11b35 100644
--- a/tests/python/unittest/test_numpy_op.py
+++ b/tests/python/unittest/test_numpy_op.py
@@ -26,7 +26,7 @@ import platform
 import mxnet as mx
 import scipy.stats as ss
 import scipy.special as scipy_special
-from nose.tools import assert_raises
+import pytest
 from mxnet import np, npx
 from mxnet.gluon import HybridBlock
 from mxnet.base import MXNetError
@@ -34,7 +34,7 @@ from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndar
 from mxnet.test_utils import check_numeric_gradient, use_np, collapse_sum_like
 from mxnet.test_utils import new_matrix_with_real_eigvals_nd
 from mxnet.test_utils import new_sym_matrix_with_real_eigvals_nd
-from common import assertRaises, with_seed
+from common import assertRaises, with_seed, retry
 import random
 from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf
 from mxnet.numpy_op_signature import _get_builtin_op
@@ -552,7 +552,7 @@ def test_np_matmul():
     for shape_a, shape_b in bad_shapes:
         a = np.random.uniform(size=shape_a)
         b = np.random.uniform(size=shape_b)
-        assert_raises(MXNetError, lambda: np.matmul(a, b))
+        pytest.raises(MXNetError, lambda: np.matmul(a, b))
 
 
 @with_seed()
@@ -1479,7 +1479,7 @@ def test_npx_batch_dot():
         for dtype in dtypes:
             lhs_val = mx.np.array(_np.random.uniform(-1.0, 1.0, lhs_shape), dtype=dtype)
             rhs_val = mx.np.array(_np.random.uniform(-1.0, 1.0, rhs_shape), dtype=dtype)
-            assert_raises(MXNetError, lambda: mx.npx.batch_dot(lhs_val, rhs_val,
+            pytest.raises(MXNetError, lambda: mx.npx.batch_dot(lhs_val, rhs_val,
                                                                transpose_a=transpose_a,
                                                                transpose_b=transpose_b))
 
@@ -1964,8 +1964,8 @@ def test_np_transpose():
                             assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-3, atol=1e-5, use_broadcast=False)
     # Test for error raising
     dat = np.random.normal(0, 1, (3, 4, 5), dtype=np.float32)
-    assert_raises(ValueError, lambda: dat.transpose((0, 0, 1)))
-    assert_raises(MXNetError, lambda: dat.transpose((0, 1, 3)))
+    pytest.raises(ValueError, lambda: dat.transpose((0, 0, 1)))
+    pytest.raises(MXNetError, lambda: dat.transpose((0, 1, 3)))
 
 
 
@@ -4490,6 +4490,7 @@ def test_np_cumsum():
 
 @with_seed()
 @use_np
+@pytest.mark.skip(reason='Skipped as the test is flaky and the feature causes curand error. Tracked in #18100')
 def test_np_histogram():
     shapes = [(), (3, 4), (3, 0)]
 
@@ -4507,6 +4508,7 @@ def test_np_histogram():
 
 @with_seed()
 @use_np
+@pytest.mark.skip(reason='Skipped as the test is flaky and the feature causes curand error. Tracked in #18100')
 def test_np_choice():
     class TestUniformChoice(HybridBlock):
         def __init__(self, sample_size, replace):
@@ -5683,7 +5685,7 @@ def test_np_linalg_lstsq():
         def __init__(self, rcond):
             super(TestLstsq, self).__init__()
             self._rcond = rcond
-        
+
         def hybrid_forward(self, F, a, b, rcond='warn'):
             return F.np.linalg.lstsq(a, b, rcond=self._rcond)
 
@@ -6446,6 +6448,7 @@ def test_np_full():
 
 @with_seed()
 @use_np
+@pytest.mark.skip(reason='Skipped as the test is flaky and the feature causes curand error. Tracked in #18100')
 def test_np_full_like():
     class TestFullLike(HybridBlock):
         def __init__(self, fill_value, dtype, ctx):
@@ -7080,10 +7083,10 @@ def test_np_tril_indices():
             if m is None:
                 m = n
             self._m = m
-        
+
         def hybrid_forward(self, F, x, *args, **kwargs):
             return x, F.np.tril_indices(n=self._n, k=self._k, m=self._m)
-    
+
     for n in _np.random.random_integers(-10, 50, 2):
         for k in _np.random.random_integers(-50, 50, 2):
             for m in _np.random.random_integers(-10, 50, 2):
@@ -7104,7 +7107,7 @@ def test_np_tril_indices():
                         np_data[np_out] = -10
                         mx_data[mx_out] = -10
                         assert same(np_data, mx_data.asnumpy())
-                        
+
 
 @with_seed()
 @use_np
@@ -7443,6 +7446,7 @@ def test_np_einsum():
 
 @with_seed()
 @use_np
+@pytest.mark.skip(reason='Skipped as the test is flaky and the feature causes curand error. Tracked in #18100')
 def test_np_diagflat():
     class TestDiagflat(HybridBlock):
         def __init__(self, k=0):
@@ -7818,7 +7822,7 @@ def test_np_median():
         a = np.random.uniform(-1.0, 1.0, size=a_shape)
         np_out = _np.median(a.asnumpy(), axis=axis, keepdims=keepdims)
         mx_out = test_median(a)
-        
+
         assert mx_out.shape == np_out.shape
         assert_almost_equal(mx_out.asnumpy(), np_out, atol=atol, rtol=rtol)
 
@@ -8834,10 +8838,10 @@ def test_np_interp():
             self._left = left
             self._right = right
             self._period = period
-        
+
         def hybrid_forward(self, F, x, xp, fp):
             return F.np.interp(x, xp, fp, left=self._left, right=self._right, period=self._period)
-    
+
     class TestInterpScalar(HybridBlock):
         def __init__(self, x=None, left=None, right=None, period=None):
             super(TestInterpScalar, self).__init__()
@@ -8845,7 +8849,7 @@ def test_np_interp():
             self._left = left
             self._right = right
             self._period = period
-        
+
         def hybrid_forward(self, F, xp, fp):
             return F.np.interp(self._x, xp, fp, left=self._left, right=self._right, period=self._period)
 
@@ -8872,13 +8876,13 @@ def test_np_interp():
         else:
             x = np.random.uniform(0, 100, size=xshape).astype(xtype)
             xp = np.sort(np.random.choice(100, dsize, replace=False).astype(dtype))
-            fp = np.random.uniform(-50, 50, size=dsize).astype(dtype) 
+            fp = np.random.uniform(-50, 50, size=dsize).astype(dtype)
         np_x = x.asnumpy()
         if x_scalar and xshape == ():
             x = x.item()
             np_x = x
             test_interp = TestInterpScalar(x=x, left=left, right=right, period=period)
-        else: 
+        else:
             test_interp = TestInterp(left=left, right=right, period=period)
         if hybridize:
             test_interp.hybridize()
@@ -8944,6 +8948,7 @@ def test_np_bincount():
 
 @with_seed()
 @use_np
+@pytest.mark.skip(reason='Test hangs. Tracked in #18144')
 def test_np_empty_like():
     class TestEmptyLike(HybridBlock):
         def __init__(self, dtype, order, subok):
@@ -9268,7 +9273,7 @@ def test_np_rollaxis():
             super(TestRollaxis, self).__init__()
             self._axis = axis
             self._start = start
-             
+
         def hybrid_forward(self, F, a, *args, **kwargs):
             return F.np.rollaxis(a, axis=self._axis, start=self._start)
 
@@ -9327,7 +9332,3 @@ def test_npx_stop_gradient():
                 elif grad_req == 'add':
                     assert_almost_equal(new_grad, old_grad + 1)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
\ No newline at end of file
diff --git a/tests/python/unittest/test_operator.py b/tests/python/unittest/test_operator.py
index 61e385b..4e7c75c 100644
--- a/tests/python/unittest/test_operator.py
+++ b/tests/python/unittest/test_operator.py
@@ -29,9 +29,9 @@ from numpy.testing import assert_allclose, assert_array_equal
 from mxnet.test_utils import *
 from mxnet.operator import *
 from mxnet.base import py_str, MXNetError, _as_list
-from common import setup_module, with_seed, teardown, assert_raises_cudnn_not_satisfied, assert_raises_cuda_not_satisfied, assertRaises
+from common import setup_module, with_seed, teardown_module, assert_raises_cudnn_not_satisfied, assert_raises_cuda_not_satisfied, assertRaises
 from common import run_in_spawned_process
-from nose.tools import assert_raises, ok_
+import pytest
 import unittest
 import os
 
@@ -6069,7 +6069,7 @@ def test_custom_op_exc():
         b = mx.nd.zeros((1, 4))
         c = mx.nd.Custom(a, b, op_type='Dot1')
         c.wait_to_read()
-    assert_raises(MXNetError, custom_exc1)
+    pytest.raises(MXNetError, custom_exc1)
 
     # 2. error in pushing operator to engine
     def custom_exc2():
@@ -6081,7 +6081,7 @@ def test_custom_op_exc():
         # trigger error by invalid input shapes of operands
         c = mx.nd.Custom(a, b, op_type='Dot2')
         c.wait_to_read()
-    assert_raises(MXNetError, custom_exc2)
+    pytest.raises(MXNetError, custom_exc2)
 
     # 3. error in real execution
     if default_context().device_type == 'cpu':
@@ -6098,7 +6098,7 @@ def test_custom_op_exc():
             b = mx.nd.zeros((1, 2))
             c = mx.nd.Custom(a, b, op_type='Dot3')
             c.wait_to_read()
-        assert_raises(MXNetError, custom_exc3)
+        pytest.raises(MXNetError, custom_exc3)
 
         def custom_exc4():
             def f(in_data, out_data):
@@ -6112,7 +6112,7 @@ def test_custom_op_exc():
             b = mx.nd.zeros((1, 2))
             c = mx.nd.Custom(a, b, op_type='Dot4')
             c.wait_to_read()
-        assert_raises(MXNetError, custom_exc4)
+        pytest.raises(MXNetError, custom_exc4)
 
 
 @with_seed()
@@ -7157,11 +7157,11 @@ def test_dropout_reproducibility():
 
     assert_almost_equal(result1.asnumpy(), result5.asnumpy())
     assert_almost_equal(result2.asnumpy(), result6.asnumpy())
-    with assert_raises(AssertionError):
+    with pytest.raises(AssertionError):
         assert_almost_equal(result1.asnumpy(), result2.asnumpy())
-    with assert_raises(AssertionError):
+    with pytest.raises(AssertionError):
         assert_almost_equal(result1.asnumpy(), result3.asnumpy())
-    with assert_raises(AssertionError):
+    with pytest.raises(AssertionError):
         assert_almost_equal(result2.asnumpy(), result4.asnumpy())
 
 @unittest.skip("test fails intermittently. temporarily disabled till it gets fixed. tracked at https://github.com/apache/incubator-mxnet/issues/11290")
@@ -7704,8 +7704,8 @@ def test_zero_size_min_max():
         a = mx.nd.zeros(shape=(5, 0))
         a.max()
 
-    assert_raises(MXNetError, min)
-    assert_raises(MXNetError, max)
+    pytest.raises(MXNetError, min)
+    pytest.raises(MXNetError, max)
 
 
 @with_seed()
@@ -9343,18 +9343,18 @@ def test_add_n():
 
 def test_get_all_registered_operators():
     ops = get_all_registered_operators()
-    ok_(isinstance(ops, list))
-    ok_(len(ops) > 0)
-    ok_('Activation' in ops)
+    assert isinstance(ops, list)
+    assert len(ops) > 0
+    assert 'Activation' in ops
 
 
 def test_get_operator_arguments():
     operator_arguments = get_operator_arguments('Activation')
-    ok_(isinstance(operator_arguments, OperatorArguments))
-    ok_(operator_arguments.names == ['data', 'act_type'])
-    ok_(operator_arguments.types
-        == ['NDArray-or-Symbol', "{'relu', 'sigmoid', 'softrelu', 'softsign', 'tanh'}, required"])
-    ok_(operator_arguments.narg == 2)
+    assert isinstance(operator_arguments, OperatorArguments)
+    assert operator_arguments.names == ['data', 'act_type']
+    assert operator_arguments.types \
+        == ['NDArray-or-Symbol', "{'relu', 'sigmoid', 'softrelu', 'softsign', 'tanh'}, required"]
+    assert operator_arguments.narg == 2
 
 
 def test_transpose_infer_shape_back():
@@ -10016,6 +10016,3 @@ def test_broadcast_ops_on_misaligned_input_oneside():
                 mx.nd.waitall()
                 assert_almost_equal(f, expected)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_optimizer.py b/tests/python/unittest/test_optimizer.py
old mode 100755
new mode 100644
index d3545ce..bf04179
--- a/tests/python/unittest/test_optimizer.py
+++ b/tests/python/unittest/test_optimizer.py
@@ -22,10 +22,10 @@ import mxnet as mx
 import mxnet.lr_scheduler as lr_scheduler
 from mxnet import gluon
 import unittest
-from nose.tools import raises
+import pytest
 import math
 from mxnet.test_utils import *
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 @with_seed()
 def test_learning_rate():
@@ -44,7 +44,7 @@ def test_learning_rate():
     assert o3.learning_rate == 1024
 
 
-@raises(UserWarning)
+@pytest.mark.xfail(raises=UserWarning)
 @with_seed()
 def test_learning_rate_expect_user_warning():
     lr_s = lr_scheduler.FactorScheduler(step=1)
@@ -284,7 +284,7 @@ def test_lars():
 def test_lamb():
     opt1 = mx.optimizer.LAMB
     opt2 = mx.optimizer.LAMB
-    
+
     shapes = [(3, 4, 5), (10, 4), (7,)]
     beta1_options = [{}, {'beta1': 0.5}]
     beta2_options = [{}, {'beta2': 0.8}]
@@ -953,8 +953,3 @@ def test_cosine_scheduler():
     np.testing.assert_almost_equal(cosine_sched(steps), final_lr)
     assert (cosine_sched(500) > 1.5)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
-
diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py
index fc2fbf6..325b830 100644
--- a/tests/python/unittest/test_predictor.py
+++ b/tests/python/unittest/test_predictor.py
@@ -26,7 +26,7 @@ import mxnet as mx
 import mxnet.ndarray as nd
 from mxnet import gluon
 from mxnet.test_utils import assert_almost_equal
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 @with_seed()
 def test_predictor():
@@ -81,7 +81,3 @@ def test_load_ndarray():
     for k in nd_data.keys():
         assert_almost_equal(nd_data[k].asnumpy(), nd_load[k], rtol=1e-5, atol=1e-6)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_profiler.py b/tests/python/unittest/test_profiler.py
index 5a0baca..bf74504 100644
--- a/tests/python/unittest/test_profiler.py
+++ b/tests/python/unittest/test_profiler.py
@@ -263,7 +263,7 @@ def test_aggregate_stats_sorting():
         for domain_name, domain in target_dict['Time'].items():
             lst = [item[sort_by_options[sort_by]] for item_name, item in domain.items()]
             check_ascending(lst, ascending)
-        # Memory items do not have stat 'Total' 
+        # Memory items do not have stat 'Total'
         if sort_by != 'total':
             for domain_name, domain in target_dict['Memory'].items():
                 lst = [item[sort_by_options[sort_by]] for item_name, item in domain.items()]
@@ -372,7 +372,7 @@ def check_custom_operator_profiling_multiple_custom_ops_output(debug_str):
 
 def custom_operator_profiling_multiple_custom_ops(seed, mode, file_name):
     class MyAdd(mx.operator.CustomOp):
-        def forward(self, is_train, req, in_data, out_data, aux):        
+        def forward(self, is_train, req, in_data, out_data, aux):
             self.assign(out_data[0], req[0], in_data[0] + 1)
 
         def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
@@ -610,7 +610,3 @@ def test_gpu_memory_profiler_gluon():
                row['Attribute Name'] == "<unk>:":
                 assert False, "Unknown allocation entry has been encountered"
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_random.py b/tests/python/unittest/test_random.py
index efcf16d..79fab10 100644
--- a/tests/python/unittest/test_random.py
+++ b/tests/python/unittest/test_random.py
@@ -19,12 +19,13 @@ import os
 import math
 import itertools
 import mxnet as mx
-from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, retry, assert_almost_equal
+from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, assert_almost_equal
 import numpy as np
 import random as rnd
-from common import setup_module, with_seed, random_seed, teardown
+from common import setup_module, with_seed, retry, random_seed, teardown_module
 import scipy.stats as ss
 import unittest
+import pytest
 from mxnet.test_utils import *
 
 def same(a, b):
@@ -339,7 +340,7 @@ def check_with_device(device, dtype):
            un1 = np.maximum(un1, 1e-1)
         if name == 'uniform':
            un1 = np.minimum(np.maximum(un1.reshape((un1.shape[0],un1.shape[1],-1)), p1.reshape((p1.shape[0],p1.shape[1],-1))+1e-4),
-                            p2.reshape((p2.shape[0],p2.shape[1],-1))-1e-4).reshape(un1.shape) 
+                            p2.reshape((p2.shape[0],p2.shape[1],-1))-1e-4).reshape(un1.shape)
         for use_log in [False, True]:
             test_pdf = symbol(v0, v1, is_log=use_log) if single_param else symbol(v0, v1, v2, is_log=use_log)
             forw_atol  = 1e-7 if dtype != np.float16 else 1e-3
@@ -349,7 +350,7 @@ def check_with_device(device, dtype):
             if single_param:
                 res = pdffunc(un1.reshape((un1.shape[0],un1.shape[1],-1)),
                     p1.reshape((p1.shape[0],p1.shape[1],-1))).reshape(un1.shape)
-                if use_log: 
+                if use_log:
                     res = np.log(res)
                 check_symbolic_forward(test_pdf, [un1, p1], [res], atol=forw_atol, rtol=forw_rtol, dtype=dtype)
                 if dtype == np.float64:
@@ -558,48 +559,47 @@ def test_parallel_random_seed_setting_for_context():
         for i in range(1, len(samples_sym)):
             assert same(samples_sym[i - 1], samples_sym[i])
 
-@retry(5)
 @with_seed()
-def test_sample_multinomial():
-    for dtype in ['uint8', 'int32', 'float16', 'float32', 'float64']: # output array types
-        for x in [mx.nd.array([[0,1,2,3,4],[4,3,2,1,0]])/10.0, mx.nd.array([0,1,2,3,4])/10.0]:
-            dx = mx.nd.ones_like(x)
-            mx.contrib.autograd.mark_variables([x], [dx])
-            # Adding rtol and increasing samples needed to pass with seed 2951820647
-            samples = 10000
-            with mx.autograd.record():
-                y, prob = mx.nd.random.multinomial(x, shape=samples, get_prob=True, dtype=dtype)
-                r = prob * 5
-                r.backward()
-
-            assert(np.dtype(dtype) == y.dtype)
-            y = y.asnumpy()
-            x = x.asnumpy()
-            dx = dx.asnumpy()
-            if len(x.shape) is 1:
-                x = x.reshape((1, x.shape[0]))
-                dx = dx.reshape(1, dx.shape[0])
-                y = y.reshape((1, y.shape[0]))
-                prob = prob.reshape((1, prob.shape[0]))
-            for i in range(x.shape[0]):
-                freq = np.bincount(y[i,:].astype('int32'), minlength=5)/np.float32(samples)*x[i,:].sum()
-                assert_almost_equal(freq, x[i], rtol=0.20, atol=1e-1)
-                rprob = x[i][y[i].astype('int32')]/x[i].sum()
-                assert_almost_equal(np.log(rprob), prob.asnumpy()[i], atol=1e-5)
-
-                real_dx = np.zeros((5,))
-                for j in range(samples):
-                    real_dx[int(y[i][j])] += 5.0 / rprob[j]
-                assert_almost_equal(real_dx, dx[i, :], rtol=1e-4, atol=1e-5)
-    for dtype in ['uint8', 'float16', 'float32']:
-        # Bound check for the output data types. 'int32' and 'float64' require large memory so are skipped.
-        x = mx.nd.zeros(2 ** 25)  # Larger than the max integer in float32 without precision loss.
-        bound_check = False
-        try:
-            y = mx.nd.random.multinomial(x, dtype=dtype)
-        except mx.MXNetError as e:
-            bound_check = True
-        assert bound_check
+@pytest.mark.parametrize('dtype', ['uint8', 'int32', 'float16', 'float32', 'float64'])
+@pytest.mark.parametrize('x', [[[0,1,2,3,4],[4,3,2,1,0]], [0,1,2,3,4]])
+def test_sample_multinomial(dtype, x):
+    x = mx.nd.array(x) / 10.0
+    dx = mx.nd.ones_like(x)
+    mx.contrib.autograd.mark_variables([x], [dx])
+    # Adding rtol and increasing samples needed to pass with seed 2951820647
+    samples = 10000
+    with mx.autograd.record():
+        y, prob = mx.nd.random.multinomial(x, shape=samples, get_prob=True, dtype=dtype)
+        r = prob * 5
+        r.backward()
+
+    assert(np.dtype(dtype) == y.dtype)
+    y = y.asnumpy()
+    x = x.asnumpy()
+    dx = dx.asnumpy()
+    if len(x.shape) is 1:
+        x = x.reshape((1, x.shape[0]))
+        dx = dx.reshape(1, dx.shape[0])
+        y = y.reshape((1, y.shape[0]))
+        prob = prob.reshape((1, prob.shape[0]))
+    for i in range(x.shape[0]):
+        freq = np.bincount(y[i,:].astype('int32'), minlength=5)/np.float32(samples)*x[i,:].sum()
+        assert_almost_equal(freq, x[i], rtol=0.20, atol=1e-1)
+        rprob = x[i][y[i].astype('int32')]/x[i].sum()
+        assert_almost_equal(np.log(rprob), prob.asnumpy()[i], atol=1e-5)
+
+        real_dx = np.zeros((5,))
+        for j in range(samples):
+            real_dx[int(y[i][j])] += 5.0 / rprob[j]
+        assert_almost_equal(real_dx, dx[i, :], rtol=1e-4, atol=1e-5)
+
+@pytest.mark.parametrize('dtype', ['uint8', 'float16', 'float32'])
+@with_seed()
+@retry(5)
+@pytest.mark.xfail(raises=mx.MXNetError)
+def test_sample_multinomial_bound_check(dtype):
+    # Larger than the max integer in float32 without precision loss.
+    y = mx.nd.random.multinomial(mx.nd.zeros(2 ** 25), dtype=dtype)
 
 # Test the generators with the chi-square testing
 @with_seed()
@@ -1035,7 +1035,3 @@ def test_sample_multinomial_num_outputs():
     assert isinstance(out, list)
     assert len(out) == 2
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_recordio.py b/tests/python/unittest/test_recordio.py
index 81561b9..6db54d5 100644
--- a/tests/python/unittest/test_recordio.py
+++ b/tests/python/unittest/test_recordio.py
@@ -22,7 +22,7 @@ import numpy as np
 import tempfile
 import random
 import string
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 
 @with_seed()
 def test_recordio():
diff --git a/tests/python/unittest/test_rnn.py b/tests/python/unittest/test_rnn.py
index a558825..ab333f9 100644
--- a/tests/python/unittest/test_rnn.py
+++ b/tests/python/unittest/test_rnn.py
@@ -308,7 +308,4 @@ def test_encode_sentences():
     print(result, vocab)
     assert vocab == {'a': 1, 'b': 2, 'c': 3, 'UNK': 0}
     assert result == [[1,2,3],[2,3,0]]
-    
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
+
diff --git a/tests/python/unittest/test_runtime.py b/tests/python/unittest/test_runtime.py
index 82e2314..0bd4d4c 100644
--- a/tests/python/unittest/test_runtime.py
+++ b/tests/python/unittest/test_runtime.py
@@ -19,14 +19,14 @@ import mxnet as mx
 import sys
 from mxnet.runtime import *
 from mxnet.base import MXNetError
-from nose.tools import *
+import pytest
 
 
 def test_features():
     features = Features()
     print(features)
-    ok_('CUDA' in features)
-    ok_(len(features) >= 30)
+    assert 'CUDA' in features
+    assert len(features) >= 30
 
 
 def test_is_singleton():
@@ -39,17 +39,13 @@ def test_is_enabled():
     features = Features()
     for f in features:
         if features[f].enabled:
-            ok_(features.is_enabled(f))
+            assert features.is_enabled(f)
         else:
-            ok_(not features.is_enabled(f))
+            assert not features.is_enabled(f)
 
 
-@raises(RuntimeError)
+@pytest.mark.xfail(raises=RuntimeError)
 def test_is_enabled_not_existing():
     features = Features()
     features.is_enabled('this girl is on fire')
 
-
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_sparse_ndarray.py b/tests/python/unittest/test_sparse_ndarray.py
index 9a1fce4..de06c7b 100644
--- a/tests/python/unittest/test_sparse_ndarray.py
+++ b/tests/python/unittest/test_sparse_ndarray.py
@@ -19,7 +19,7 @@ import pickle as pkl
 
 from mxnet.ndarray import NDArray
 from mxnet.test_utils import *
-from common import setup_module, with_seed, random_seed, teardown
+from common import setup_module, with_seed, random_seed, teardown_module
 from mxnet.base import mx_real_t
 from numpy.testing import assert_allclose
 import numpy.random as rnd
@@ -1056,6 +1056,3 @@ def test_sparse_getnnz():
         for a in axis:
             check_sparse_getnnz(d, a)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_sparse_operator.py b/tests/python/unittest/test_sparse_operator.py
index 4c4e3db..f23f594 100644
--- a/tests/python/unittest/test_sparse_operator.py
+++ b/tests/python/unittest/test_sparse_operator.py
@@ -17,7 +17,7 @@
 
 from mxnet.test_utils import *
 from mxnet.base import MXNetError
-from common import setup_module, with_seed, teardown, assertRaises
+from common import setup_module, with_seed, teardown_module, assertRaises
 import random
 import warnings
 
@@ -2346,6 +2346,3 @@ def test_reshape_backward_fallback():
 
     assert_almost_equal(grad_w_nd.asnumpy(), expected_grad_nd)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_subgraph.py b/tests/python/unittest/test_subgraph.py
index 3da125a..b1a7aa3 100644
--- a/tests/python/unittest/test_subgraph.py
+++ b/tests/python/unittest/test_subgraph.py
@@ -21,7 +21,7 @@ import numpy as np
 import mxnet as mx
 import copy
 from mxnet.test_utils import *
-from common import setup_module, with_seed, teardown
+from common import setup_module, with_seed, teardown_module
 from mxnet.gluon.model_zoo.vision import get_model
 
 def make_subgraph(subg, *args):
@@ -190,6 +190,3 @@ def test_subgraph_with_customOp():
     c.bind(mx.cpu(), {'a': inp}).forward()
     mx.nd.waitall()
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_subgraph_op.py b/tests/python/unittest/test_subgraph_op.py
index e414a98..bffb38e 100644
--- a/tests/python/unittest/test_subgraph_op.py
+++ b/tests/python/unittest/test_subgraph_op.py
@@ -41,7 +41,7 @@ def network_structure_2():
     ret1 = mx.sym.cos(ret)
     ret2 = mx.sym.sin(ret)
     ret = ret1 + ret2
-    return (ret, ['data'], [(2, 3, 10, 10)]) 
+    return (ret, ['data'], [(2, 3, 10, 10)])
 
 def network_structure_3():
     # this tests whether the partitioned sym can distinguish in_args and aux_states
@@ -74,7 +74,7 @@ def network_structure_6():
     data3 = mx.sym.sin(data2)
     conv = mx.sym.Convolution(data=data1, weight=data3, kernel=(2, 2), num_filter=1)
     return (conv, ['data1'], [(3, 3, 10, 10)])
-        
+
 def network_structure_7():
     # in this graph, the subgraph node and the other two external nodes form a cycle
     data = mx.sym.Variable('data', shape=(1,))
@@ -85,7 +85,7 @@ def network_structure_7():
     ret = ret1 + ret2
     return (ret, ['data'], [(1,)])
 
-def get_graphs(): 
+def get_graphs():
     return [
             (network_structure_1(), ['Convolution']),
             (network_structure_2(), ['exp', 'sin', '_Plus', 'elemwise_add', '_plus']),
@@ -271,7 +271,7 @@ def check_subgraph_exe5(sym, subgraph_backend, op_names):
         assert_almost_equal((outputs1[i] - outputs2[i]).abs().sum().asnumpy(), np.zeros(shape=(1,)))
 
 def check_subgraph_exe6(sym, subgraph_backend, op_names):
-    """Call optimize_for to trigger graph partitioning with shapes/types, then simple_bind 
+    """Call optimize_for to trigger graph partitioning with shapes/types, then simple_bind
     and compare results of the partitioned sym and the original sym."""
     # simple_bind
     exe1 = sym.simple_bind(ctx=mx.current_context(), grad_req='null')
@@ -340,17 +340,17 @@ def check_subgraph_exe8(sym, subgraph_backend, op_names):
 
     exe2 = part_sym.bind(ctx=mx.current_context(), args=arg_array, aux_states=aux_array, grad_req='null')
     exe2.forward()
-    
+
     # compare outputs
     outputs1 = exe1.outputs
     outputs2 = exe2.outputs
     assert len(outputs1) == len(outputs2)
     for i in range(len(outputs1)):
         assert_almost_equal((outputs1[i] - outputs2[i]).abs().sum().asnumpy(), np.zeros(shape=(1,)))
-    
+
 def check_subgraph_exe9(sym, subgraph_backend, op_names):
-    """Call hybridize() to partition the graph, and then compare results of the partitioned 
-    sym and the original sym. Here do an inference before hybridizing with the subgraph_backend 
+    """Call hybridize() to partition the graph, and then compare results of the partitioned
+    sym and the original sym. Here do an inference before hybridizing with the subgraph_backend
     which means we'll pass shapes/types"""
     # create Gluon block for given symbol
     inputs = [mx.sym.var(i, dtype=mx_real_t) for i in sym[1]]
@@ -487,6 +487,3 @@ def test_subgraph_backend_gluon_ext2():
     for i in range(len(outputs1)):
         assert_almost_equal((outputs1[i] - outputs2[i]).abs().sum().asnumpy(), np.zeros(shape=(1,)))
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_symbol.py b/tests/python/unittest/test_symbol.py
index c913f5c..7582661 100644
--- a/tests/python/unittest/test_symbol.py
+++ b/tests/python/unittest/test_symbol.py
@@ -316,14 +316,14 @@ def test_zero_prop():
         data = data * data
 
     exe = data.simple_bind(ctx=mx.cpu(), data=(10, 3, 256, 256))
-    big = int(re.search('Total (\d+) MB allocated', exe.debug_str()).group(1))
+    big = int(re.search(r'Total (\d+) MB allocated', exe.debug_str()).group(1))
 
     exe = data.simple_bind(ctx=mx.cpu(), data=(10, 3, 256, 256), grad_req='null')
-    small1 = int(re.search('Total (\d+) MB allocated', exe.debug_str()).group(1))
+    small1 = int(re.search(r'Total (\d+) MB allocated', exe.debug_str()).group(1))
 
     data = mx.sym.stop_gradient(data)
     exe = data.simple_bind(ctx=mx.cpu(), data=(10, 3, 256, 256))
-    small2 = int(re.search('Total (\d+) MB allocated', exe.debug_str()).group(1))
+    small2 = int(re.search(r'Total (\d+) MB allocated', exe.debug_str()).group(1))
 
     assert big > small2
     assert small1 == small2
@@ -351,7 +351,7 @@ def test_zero_prop2():
 
 
 def test_simple_bind_incomplete_shape_inference_in_one_forward_pass():
-    """This is a special case that results in shape inference
+    r"""This is a special case that results in shape inference
     failure after moving simple_bind logic from frontend to backend.
     Added here for testing against the network similar to the following one.
 
@@ -559,6 +559,3 @@ def test_infershape_happens_for_all_ops_in_graph():
 
     assert False
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_test_utils.py b/tests/python/unittest/test_test_utils.py
index 49f0b93..98e7d94 100644
--- a/tests/python/unittest/test_test_utils.py
+++ b/tests/python/unittest/test_test_utils.py
@@ -19,10 +19,11 @@ import os
 import tempfile
 
 import mxnet as mx
-from nose.tools import *
 
+import pytest
 
-@raises(Exception)
+
+@pytest.mark.xfail(raises=Exception)
 def test_download_retries():
     mx.test_utils.download("http://doesnotexist.notfound")
 
@@ -31,4 +32,4 @@ def test_download_successful():
     tmpfile = os.path.join(tmp, 'README.md')
     mx.test_utils.download("https://raw.githubusercontent.com/apache/incubator-mxnet/master/README.md",
                            fname=tmpfile)
-    assert os.path.getsize(tmpfile) > 100
\ No newline at end of file
+    assert os.path.getsize(tmpfile) > 100
diff --git a/tests/python/unittest/test_thread_local.py b/tests/python/unittest/test_thread_local.py
index 63d97f1..7e875c8 100644
--- a/tests/python/unittest/test_thread_local.py
+++ b/tests/python/unittest/test_thread_local.py
@@ -221,7 +221,3 @@ def test_np_global_shape():
     finally:
         set_np_shape(0)
 
-
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_tvm_op.py b/tests/python/unittest/test_tvm_op.py
index e325edc..55bb7cc 100644
--- a/tests/python/unittest/test_tvm_op.py
+++ b/tests/python/unittest/test_tvm_op.py
@@ -67,6 +67,3 @@ def test_tvm_broadcast_add():
             assert same(a.grad.asnumpy(), expected_grad_a)
             assert same(b.grad.asnumpy(), expected_grad_b)
 
-if __name__ == '__main__':
-    import nose
-    nose.runmodule()
diff --git a/tests/python/unittest/test_viz.py b/tests/python/unittest/test_viz.py
index 1321099..5c9b78a 100644
--- a/tests/python/unittest/test_viz.py
+++ b/tests/python/unittest/test_viz.py
@@ -62,6 +62,3 @@ def test_plot_network():
     assert "There are multiple variables with the same name in your graph" in str(w[-1].message)
     assert "fc" in str(w[-1].message)
 
-if __name__ == "__main__":
-    import nose
-    nose.runmodule()
diff --git a/tests/requirements.txt b/tests/requirements.txt
deleted file mode 100644
index e16b764..0000000
--- a/tests/requirements.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# Requirements for tests, those are installed before running on the virtualenv
-# Requirements for tests run within the qemu requirement see ci/qemu/test_requirements.txt
-mock
-nose
-nose-timer
-ipython
-numpy
-scipy
diff --git a/tools/caffe_converter/test_converter.py b/tools/caffe_converter/test_converter.py
old mode 100755
new mode 100644
diff --git a/tools/dependencies/README.md b/tools/dependencies/README.md
index c45f333..5cd4efd 100644
--- a/tools/dependencies/README.md
+++ b/tools/dependencies/README.md
@@ -204,7 +204,7 @@ pip install -e python
 export NCCL_DEBUG=VERSION
 vim tests/python/gpu/test_nccl.py
 # Remove @unittest.skip("Test requires NCCL library installed and enabled during build") then run
-nosetests --verbose tests/python/gpu/test_nccl.py
+pytest --verbose tests/python/gpu/test_nccl.py
 # test_nccl.test_nccl_pushpull ... NCCL version 2.4.2+cuda10.0
 # ok
 # ----------------------------------------------------------------------
diff --git a/tools/diagnose.py b/tools/diagnose.py
old mode 100755
new mode 100644
diff --git a/tools/flakiness_checker.py b/tools/flakiness_checker.py
index 79fa3b1..85eae14 100644
--- a/tools/flakiness_checker.py
+++ b/tools/flakiness_checker.py
@@ -39,7 +39,7 @@ def run_test_trials(args):
 
     new_env = os.environ.copy()
     new_env["MXNET_TEST_COUNT"] = str(args.num_trials)
-    
+
     if args.seed is None:
         logging.info("No test seed provided, using random seed")
     else:
@@ -47,17 +47,17 @@ def run_test_trials(args):
 
     verbosity = "--verbosity=" + str(args.verbosity)
 
-    code = subprocess.call(["nosetests", verbosity, test_path], 
+    code = subprocess.call(["pytest", verbosity, test_path],
                            env = new_env)
-    
-    logging.info("Nosetests terminated with exit code %d", code)
+
+    logging.info("Test terminated with exit code %d", code)
 
 def find_test_path(test_file):
     """Searches for the test file and returns the path if found
     As a default, the currend working directory is the top of the search.
     If a directory was provided as part of the argument, the directory will be
     joined with cwd unless it was an absolute path, in which case, the
-    absolute path will be used instead. 
+    absolute path will be used instead.
     """
     test_file += ".py"
     test_path = os.path.split(test_file)
@@ -66,7 +66,7 @@ def find_test_path(test_file):
     for (path, dirs, files) in os.walk(top):
         if test_path[1] in files:
             return  os.path.join(path, test_path[1])
-    raise FileNotFoundError("Could not find " + test_path[1] + 
+    raise FileNotFoundError("Could not find " + test_path[1] +
                             "in directory: " + top)
 
 class NameAction(argparse.Action):
@@ -82,12 +82,12 @@ class NameAction(argparse.Action):
 
 def parse_args():
     parser = argparse.ArgumentParser(description="Check test for flakiness")
-    
+
     parser.add_argument("test", action=NameAction,
                         help="file name and and function name of test, "
                         "provided in the format: <file-name>.<test-name> "
                         "or <directory>/<file>:<test-name>")
-    
+
     parser.add_argument("-n", "--num-trials", metavar="N",
                         default=DEFAULT_NUM_TRIALS, type=int,
                         help="number of test trials, passed as "
@@ -95,11 +95,11 @@ def parse_args():
 
     parser.add_argument("-s", "--seed", type=int,
                         help="test seed, passed as MXNET_TEST_SEED, "
-                        "defaults to random seed") 
+                        "defaults to random seed")
 
     parser.add_argument("-v", "--verbosity",
                         default=DEFAULT_VERBOSITY, type=int,
-                        help="logging level, passed to nosetests")
+                        help="logging level, passed to pytest")
 
 
     args = parser.parse_args()
diff --git a/tools/im2rec.py b/tools/im2rec.py
old mode 100755
new mode 100644
diff --git a/tools/ipynb2md.py b/tools/ipynb2md.py
old mode 100755
new mode 100644
diff --git a/tools/launch.py b/tools/launch.py
old mode 100755
new mode 100644
diff --git a/tools/parse_log.py b/tools/parse_log.py
old mode 100755
new mode 100644