Repository: dfki-ric/pytransform3d (language: python)

Issue:
Remove dependency on nosetests
Will be replaced by pytest in the future

```
pytransform3d/test/test_urdf.py
15:from nose.tools import assert_raises, assert_equal, assert_true, assert_in
16:from nose import SkipTest

pytransform3d/test/test_transformations.py
33:from nose.tools import (assert_equal, assert_almost_equal,

pytransform3d/test/test_rotations.py
4:from nose.tools import (assert_almost_equal, assert_equal, assert_true,

pytransform3d/test/test_transform_manager.py
15:from nose.tools import (assert_raises_regexp, assert_equal, assert_true,
17:from nose import SkipTest

pytransform3d/test/test_coordinates.py
3:from nose.tools import assert_less_equal

pytransform3d/test/test_camera.py
6:from nose.tools import (assert_raises_regexp, assert_false, assert_in,

pytransform3d/test/test_batch_rotations.py
4:from nose.tools import (assert_almost_equal, assert_raises_regexp,

pytransform3d/test/test_plot_utils.py
2:from nose import SkipTest
11:from nose.tools import assert_equal, assert_less, assert_greater_equal

pytransform3d/transformations/_testing.py
109:    This function needs the dependency nose.
149:    from nose.tools import assert_almost_equal
```


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index f24c90741..12488b929 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -5,9 +5,8 @@ name: Python package
 
 on:
   push:
-    branches: [ master ]
-  pull_request:
-    branches: [ master ]
+    branches-ignore:
+      - gh-pages
 
 jobs:
   build:
diff --git a/setup.py b/setup.py
index eb708a9af..94fa80358 100644
--- a/setup.py
+++ b/setup.py
@@ -29,6 +29,6 @@
               "all": ["pydot", "trimesh", "open3d"],
               "doc": ["numpydoc", "sphinx", "sphinx-gallery",
                       "sphinx-bootstrap-theme"],
-              "test": ["nose", "coverage", "pytest", "pytest-cov"]
+              "test": ["pytest", "pytest-cov"]
           }
           )


Test patch:
diff --git a/pytransform3d/test/test_batch_rotations.py b/pytransform3d/test/test_batch_rotations.py
index 88ceb27f3..00a343316 100644
--- a/pytransform3d/test/test_batch_rotations.py
+++ b/pytransform3d/test/test_batch_rotations.py
@@ -1,16 +1,15 @@
 import numpy as np
 from pytransform3d import rotations as pr
 from pytransform3d import batch_rotations as pbr
-from nose.tools import (assert_almost_equal, assert_raises_regexp,
-                        assert_equal, assert_greater)
 from numpy.testing import assert_array_almost_equal
+import pytest
 
 
 def test_norm_vectors_0dims():
     random_state = np.random.RandomState(8380)
     V = random_state.randn(3)
     V_unit = pbr.norm_vectors(V)
-    assert_almost_equal(np.linalg.norm(V_unit), 1.0)
+    assert pytest.approx(np.linalg.norm(V_unit)) == 1.0
 
 
 def test_norm_vectors_1dim():
@@ -359,7 +358,7 @@ def test_quaternion_slerp_batch_sign_ambiguity():
         [pr.quaternion_dist(r, s)
          for r, s in zip(traj_q_opposing[:-1], traj_q_opposing[1:])])
 
-    assert_greater(path_length_opposing, path_length)
+    assert path_length_opposing > path_length
 
     traj_q_opposing_corrected = pbr.quaternion_slerp_batch(
         q1, q2, np.linspace(0, 1, n_steps), shortest_path=True)
@@ -368,33 +367,35 @@ def test_quaternion_slerp_batch_sign_ambiguity():
          for r, s in zip(traj_q_opposing_corrected[:-1],
                          traj_q_opposing_corrected[1:])])
 
-    assert_almost_equal(path_length_opposing_corrected, path_length)
+    assert pytest.approx(path_length_opposing_corrected) == path_length
 
 
 def test_batch_concatenate_quaternions_mismatch():
     Q1 = np.zeros((1, 2, 4))
     Q2 = np.zeros((1, 2, 3, 4))
-    assert_raises_regexp(
-        ValueError, "Number of dimensions must be the same.",
-        pbr.batch_concatenate_quaternions, Q1, Q2)
+    with pytest.raises(
+            ValueError, match="Number of dimensions must be the same."):
+        pbr.batch_concatenate_quaternions(Q1, Q2)
 
     Q1 = np.zeros((1, 2, 4, 4))
     Q2 = np.zeros((1, 2, 3, 4))
-    assert_raises_regexp(
-        ValueError, "Size of dimension 3 does not match",
-        pbr.batch_concatenate_quaternions, Q1, Q2)
+    with pytest.raises(
+            ValueError, match="Size of dimension 3 does not match"):
+        pbr.batch_concatenate_quaternions(Q1, Q2)
 
     Q1 = np.zeros((1, 2, 3, 3))
     Q2 = np.zeros((1, 2, 3, 4))
-    assert_raises_regexp(
-        ValueError, "Last dimension of first argument does not match.",
-        pbr.batch_concatenate_quaternions, Q1, Q2)
+    with pytest.raises(
+            ValueError, match="Last dimension of first argument does not "
+                              "match."):
+        pbr.batch_concatenate_quaternions(Q1, Q2)
 
     Q1 = np.zeros((1, 2, 3, 4))
     Q2 = np.zeros((1, 2, 3, 3))
-    assert_raises_regexp(
-        ValueError, "Last dimension of second argument does not match.",
-        pbr.batch_concatenate_quaternions, Q1, Q2)
+    with pytest.raises(
+            ValueError, match="Last dimension of second argument does "
+                              "not match."):
+        pbr.batch_concatenate_quaternions(Q1, Q2)
 
 
 def test_batch_concatenate_quaternions_1d():
@@ -444,7 +445,7 @@ def test_batch_convert_quaternion_conventions():
     q_wxyz_random = pr.random_quaternion(random_state)
     q_xyzw_random = pbr.batch_quaternion_xyzw_from_wxyz(q_wxyz_random)
     assert_array_almost_equal(q_xyzw_random[:3], q_wxyz_random[1:])
-    assert_equal(q_xyzw_random[3], q_wxyz_random[0])
+    assert q_xyzw_random[3] == q_wxyz_random[0]
     q_wxyz_random2 = pbr.batch_quaternion_wxyz_from_xyzw(q_xyzw_random)
     assert_array_almost_equal(q_wxyz_random, q_wxyz_random2)
 
@@ -476,10 +477,10 @@ def test_smooth_quaternion_trajectory_start_component_negative():
             q *= -1.0
         q_corrected = pbr.smooth_quaternion_trajectory(
             [q], start_component_positive=component)[0]
-        assert_greater(q_corrected[index], 0.0)
+        assert q_corrected[index] > 0.0
 
 
 def test_smooth_quaternion_trajectory_empty():
-    assert_raises_regexp(
-        ValueError, "At least one quaternion is expected",
-        pbr.smooth_quaternion_trajectory, np.zeros((0, 4)))
+    with pytest.raises(
+            ValueError, match=r"At least one quaternion is expected"):
+        pbr.smooth_quaternion_trajectory(np.zeros((0, 4)))
diff --git a/pytransform3d/test/test_camera.py b/pytransform3d/test/test_camera.py
index 62d4d4afd..89b9c3ca1 100644
--- a/pytransform3d/test/test_camera.py
+++ b/pytransform3d/test/test_camera.py
@@ -1,54 +1,54 @@
 import numpy as np
+
 from pytransform3d.camera import (make_world_line, make_world_grid, cam2sensor,
                                   sensor2img, world2image)
 from pytransform3d.rotations import active_matrix_from_intrinsic_euler_xyz
 from pytransform3d.transformations import transform_from
-from nose.tools import (assert_raises_regexp, assert_false, assert_in,
-                        assert_equal)
 from numpy.testing import assert_array_almost_equal
+import pytest
 
 
 def test_make_world_line():
     line = make_world_line(np.array([0, 0, 0]), np.array([1, 1, 1]), 11)
-    assert_equal(len(line), 11)
-    assert_in(np.array([0, 0, 0, 1]), line)
-    assert_in(np.array([0.5, 0.5, 0.5, 1]), line)
-    assert_in(np.array([1, 1, 1, 1]), line)
+    assert len(line) == 11
+    assert np.array([0, 0, 0, 1]) in line
+    assert np.array([0.5, 0.5, 0.5, 1]) in line
+    assert np.array([1, 1, 1, 1]) in line
 
 
 def test_make_world_grid():
     grid = make_world_grid(3, 3, xlim=(-1, 1), ylim=(-1, 1))
-    assert_in(np.array([-1, -1, 0, 1]), grid)
-    assert_in(np.array([-1, 0, 0, 1]), grid)
-    assert_in(np.array([-1, 1, 0, 1]), grid)
-    assert_in(np.array([0, -1, 0, 1]), grid)
-    assert_in(np.array([0, 0, 0, 1]), grid)
-    assert_in(np.array([0, 1, 0, 1]), grid)
-    assert_in(np.array([1, -1, 0, 1]), grid)
-    assert_in(np.array([1, 0, 0, 1]), grid)
-    assert_in(np.array([1, 1, 0, 1]), grid)
+    assert np.array([-1, -1, 0, 1]) in grid
+    assert np.array([-1, 0, 0, 1]) in grid
+    assert np.array([-1, 1, 0, 1]) in grid
+    assert np.array([0, -1, 0, 1]) in grid
+    assert np.array([0, 0, 0, 1]) in grid
+    assert np.array([0, 1, 0, 1]) in grid
+    assert np.array([1, -1, 0, 1]) in grid
+    assert np.array([1, 0, 0, 1]) in grid
+    assert np.array([1, 1, 0, 1]) in grid
 
 
 def test_cam2sensor_wrong_dimensions():
     P_cam = np.ones((1, 2))
-    assert_raises_regexp(ValueError, "3- or 4-dimensional points",
-                         cam2sensor, P_cam, 0.1)
+    with pytest.raises(ValueError, match="3- or 4-dimensional points"):
+        cam2sensor(P_cam, 0.1)
     P_cam = np.ones((1, 5))
-    assert_raises_regexp(ValueError, "3- or 4-dimensional points",
-                         cam2sensor, P_cam, 0.1)
+    with pytest.raises(ValueError, match="3- or 4-dimensional points"):
+        cam2sensor(P_cam, 0.1)
 
 
 def test_cam2sensor_wrong_focal_length():
     P_cam = np.ones((1, 3))
-    assert_raises_regexp(ValueError, "must be greater than 0",
-                         cam2sensor, P_cam, 0.0)
+    with pytest.raises(ValueError, match="must be greater than 0"):
+        cam2sensor(P_cam, 0.0)
 
 
 def test_cam2sensor_points_behind_camera():
     P_cam = np.ones((1, 3))
     P_cam[0, 2] = -1.0
     P_sensor = cam2sensor(P_cam, 0.1)
-    assert_false(np.any(np.isfinite(P_sensor)))
+    assert not np.any(np.isfinite(P_sensor))
 
 
 def test_cam2sensor_projection():
diff --git a/pytransform3d/test/test_coordinates.py b/pytransform3d/test/test_coordinates.py
index f00647269..bdba01d88 100644
--- a/pytransform3d/test/test_coordinates.py
+++ b/pytransform3d/test/test_coordinates.py
@@ -1,6 +1,5 @@
 import numpy as np
 import pytransform3d.coordinates as pc
-from nose.tools import assert_less_equal
 from numpy.testing import assert_array_almost_equal
 
 
@@ -23,9 +22,8 @@ def test_convert_cartesian_cylindrical():
         p = np.array([rho, phi, z])
         q = pc.cartesian_from_cylindrical(p)
         r = pc.cylindrical_from_cartesian(q)
-        assert_less_equal(0, r[0])
-        assert_less_equal(-np.pi, r[1])
-        assert_less_equal(r[1], np.pi)
+        assert 0 <= r[0]
+        assert -np.pi <= r[1] <= np.pi
         s = pc.cartesian_from_cylindrical(r)
         assert_array_almost_equal(q, s)
 
@@ -49,11 +47,9 @@ def test_convert_cartesian_spherical():
         p = np.array([rho, theta, phi])
         q = pc.cartesian_from_spherical(p)
         r = pc.spherical_from_cartesian(q)
-        assert_less_equal(0, r[0])
-        assert_less_equal(0, r[1])
-        assert_less_equal(r[1], np.pi)
-        assert_less_equal(-np.pi, r[2])
-        assert_less_equal(r[2], np.pi)
+        assert 0 <= r[0]
+        assert 0 <= r[1] <= np.pi
+        assert -np.pi <= r[2] <= np.pi
         s = pc.cartesian_from_spherical(r)
         assert_array_almost_equal(q, s)
 
diff --git a/pytransform3d/test/test_plot_utils.py b/pytransform3d/test/test_plot_utils.py
index 6c1e6393b..bc5cae6ff 100644
--- a/pytransform3d/test/test_plot_utils.py
+++ b/pytransform3d/test/test_plot_utils.py
@@ -1,24 +1,23 @@
 import numpy as np
-from nose import SkipTest
+import pytest
 try:
     import matplotlib
 except ImportError:
-    raise SkipTest("matplotlib is required for these tests")
+    pytest.skip("matplotlib is required for these tests")
 from pytransform3d.plot_utils import (
     make_3d_axis, remove_frame, Frame, LabeledFrame, Trajectory,
     plot_box, plot_sphere, plot_cylinder, plot_mesh, plot_ellipsoid,
     plot_capsule, plot_cone, plot_vector, plot_length_variable)
-from nose.tools import assert_equal, assert_less, assert_greater_equal
 from numpy.testing import assert_array_almost_equal
 
 
 def test_make_3d_axis():
     ax = make_3d_axis(2.0)
     try:
-        assert_equal(ax.name, "3d")
-        assert_equal(ax.get_xlim(), (-2, 2))
-        assert_equal(ax.get_ylim(), (-2, 2))
-        assert_equal(ax.get_zlim(), (-2, 2))
+        assert ax.name == "3d"
+        assert ax.get_xlim() == (-2, 2)
+        assert ax.get_ylim() == (-2, 2)
+        assert ax.get_zlim() == (-2, 2)
     finally:
         ax.remove()
 
@@ -29,8 +28,8 @@ def test_make_3d_axis_subplots():
     try:
         bounds1 = ax1.get_position().bounds
         bounds2 = ax2.get_position().bounds
-        assert_less(bounds1[0], bounds2[0])
-        assert_less(bounds1[2], bounds2[2])
+        assert bounds1[0] < bounds2[0]
+        assert bounds1[2] < bounds2[2]
     finally:
         ax1.remove()
         ax2.remove()
@@ -39,9 +38,9 @@ def test_make_3d_axis_subplots():
 def test_make_3d_axis_with_units():
     ax = make_3d_axis(1.0, unit="m")
     try:
-        assert_equal(ax.get_xlabel(), "X [m]")
-        assert_equal(ax.get_ylabel(), "Y [m]")
-        assert_equal(ax.get_zlabel(), "Z [m]")
+        assert ax.get_xlabel() == "X [m]"
+        assert ax.get_ylabel() == "Y [m]"
+        assert ax.get_zlabel() == "Z [m]"
     finally:
         ax.remove()
 
@@ -62,8 +61,8 @@ def test_frame():
     try:
         frame = Frame(np.eye(4), label="Frame", s=0.1)
         frame.add_frame(ax)
-        assert_equal(len(ax.lines), 4)  # 3 axes and black line to text
-        assert_equal(len(ax.texts), 1)  # label
+        assert len(ax.lines) == 4  # 3 axes and black line to text
+        assert len(ax.texts) == 1  # label
     finally:
         ax.remove()
 
@@ -73,8 +72,8 @@ def test_frame_no_indicator():
     try:
         frame = Frame(np.eye(4), label="Frame", s=0.1, draw_label_indicator=False)
         frame.add_frame(ax)
-        assert_equal(len(ax.lines), 3)  # 3 axes and omit black line to text
-        assert_equal(len(ax.texts), 1)  # label
+        assert len(ax.lines) == 3  # 3 axes and omit black line to text
+        assert len(ax.texts) == 1  # label
     finally:
         ax.remove()
 
@@ -84,8 +83,8 @@ def test_labeled_frame():
     try:
         frame = LabeledFrame(np.eye(4), label="Frame", s=0.1)
         frame.add_frame(ax)
-        assert_equal(len(ax.lines), 4)  # 3 axes and black line to text
-        assert_equal(len(ax.texts), 4)  # label and 3 axis labels
+        assert len(ax.lines) == 4  # 3 axes and black line to text
+        assert len(ax.texts) == 4  # label and 3 axis labels
     finally:
         ax.remove()
 
@@ -96,7 +95,7 @@ def test_trajectory():
         trajectory = Trajectory(
             np.array([np.eye(4), np.eye(4)]), s=0.1, n_frames=2)
         trajectory.add_trajectory(ax)
-        assert_equal(len(ax.lines), 7)  # 2 * 3 axes + connection line
+        assert len(ax.lines) == 7  # 2 * 3 axes + connection line
         #assert_equal(len(ax.artists), 1)  # arrow is not an artist anymore
     finally:
         ax.remove()
@@ -106,7 +105,7 @@ def test_plot_box():
     ax = make_3d_axis(1.0)
     try:
         plot_box(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -115,7 +114,7 @@ def test_plot_box_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_box(ax, wireframe=True)
-        assert_greater_equal(len(ax.lines), 1)
+        assert len(ax.lines) >= 1
     finally:
         ax.remove()
 
@@ -124,7 +123,7 @@ def test_plot_sphere():
     ax = make_3d_axis(1.0)
     try:
         plot_sphere(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -133,7 +132,7 @@ def test_plot_sphere_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_sphere(ax, wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -142,7 +141,7 @@ def test_plot_cylinder():
     ax = make_3d_axis(1.0)
     try:
         plot_cylinder(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -151,7 +150,7 @@ def test_plot_cylinder_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_cylinder(ax, wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -160,11 +159,11 @@ def test_plot_mesh():
     try:
         import trimesh
     except ImportError:
-        raise SkipTest("trimesh is required for this test")
+        pytest.skip("trimesh is required for this test")
     ax = make_3d_axis(1.0)
     try:
         plot_mesh(ax, filename="test/test_data/cone.stl", wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -173,11 +172,11 @@ def test_plot_mesh_wireframe():
     try:
         import trimesh
     except ImportError:
-        raise SkipTest("trimesh is required for this test")
+        pytest.skip("trimesh is required for this test")
     ax = make_3d_axis(1.0)
     try:
         plot_mesh(ax, filename="test/test_data/cone.stl", wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -186,7 +185,7 @@ def test_plot_ellipsoid():
     ax = make_3d_axis(1.0)
     try:
         plot_ellipsoid(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -195,7 +194,7 @@ def test_plot_ellipsoid_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_ellipsoid(ax, wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -204,7 +203,7 @@ def test_plot_capsule():
     ax = make_3d_axis(1.0)
     try:
         plot_capsule(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -213,7 +212,7 @@ def test_plot_capsule_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_capsule(ax, wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -222,7 +221,7 @@ def test_plot_cone():
     ax = make_3d_axis(1.0)
     try:
         plot_cone(ax, wireframe=False)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -231,7 +230,7 @@ def test_plot_cone_wireframe():
     ax = make_3d_axis(1.0)
     try:
         plot_cone(ax, wireframe=True)
-        assert_equal(len(ax.collections), 1)
+        assert len(ax.collections) == 1
     finally:
         ax.remove()
 
@@ -249,7 +248,7 @@ def test_plot_length_variable():
     ax = make_3d_axis(1.0)
     try:
         plot_length_variable(ax)
-        assert_greater_equal(len(ax.lines), 1)
-        assert_equal(len(ax.texts), 1)
+        assert len(ax.lines) >= 1
+        assert len(ax.texts) == 1
     finally:
         ax.remove()
diff --git a/pytransform3d/test/test_rotations.py b/pytransform3d/test/test_rotations.py
index 8f83c3417..a047a83a8 100644
--- a/pytransform3d/test/test_rotations.py
+++ b/pytransform3d/test/test_rotations.py
@@ -1,11 +1,8 @@
 import warnings
 import numpy as np
-from numpy.testing import assert_array_almost_equal, assert_array_equal
-from nose.tools import (assert_almost_equal, assert_equal, assert_true,
-                        assert_greater, assert_raises_regexp, assert_less,
-                        assert_false)
-
 import pytransform3d.rotations as pr
+from numpy.testing import assert_array_almost_equal, assert_array_equal
+import pytest
 
 
 def test_norm_vector():
@@ -14,13 +11,13 @@ def test_norm_vector():
     for n in range(1, 6):
         v = pr.random_vector(random_state, n)
         u = pr.norm_vector(v)
-        assert_almost_equal(np.linalg.norm(u), 1)
+        assert pytest.approx(np.linalg.norm(u)) == 1
 
 
 def test_norm_zero_vector():
     """Test normalization of zero vector."""
     normalized = pr.norm_vector(np.zeros(3))
-    assert_true(np.isfinite(np.linalg.norm(normalized)))
+    assert np.isfinite(np.linalg.norm(normalized))
 
 
 def test_norm_angle():
@@ -31,8 +28,8 @@ def test_norm_angle():
         a = a_norm + b
         assert_array_almost_equal(pr.norm_angle(a), a_norm)
 
-    assert_almost_equal(pr.norm_angle(-np.pi), np.pi)
-    assert_almost_equal(pr.norm_angle(np.pi), np.pi)
+    assert pytest.approx(pr.norm_angle(-np.pi)) == np.pi
+    assert pytest.approx(pr.norm_angle(np.pi)) == np.pi
 
 
 def test_norm_axis_angle():
@@ -95,35 +92,35 @@ def test_perpendicular_to_vectors():
     a1 = pr.norm_vector(pr.random_vector(random_state))
     b = pr.norm_vector(pr.perpendicular_to_vectors(a, a1))
     c = pr.norm_vector(pr.perpendicular_to_vectors(a, b))
-    assert_almost_equal(pr.angle_between_vectors(a, b), np.pi / 2.0)
-    assert_almost_equal(pr.angle_between_vectors(a, c), np.pi / 2.0)
-    assert_almost_equal(pr.angle_between_vectors(b, c), np.pi / 2.0)
+    assert pytest.approx(pr.angle_between_vectors(a, b)) == np.pi / 2.0
+    assert pytest.approx(pr.angle_between_vectors(a, c)) == np.pi / 2.0
+    assert pytest.approx(pr.angle_between_vectors(b, c)) == np.pi / 2.0
     assert_array_almost_equal(pr.perpendicular_to_vectors(b, c), a)
     assert_array_almost_equal(pr.perpendicular_to_vectors(c, a), b)
 
 
 def test_perpendicular_to_vector():
     """Test function to compute perpendicular to vector."""
-    assert_almost_equal(pr.angle_between_vectors(
-        pr.unitx, pr.perpendicular_to_vector(pr.unitx)), np.pi / 2.0)
-    assert_almost_equal(pr.angle_between_vectors(
-        pr.unity, pr.perpendicular_to_vector(pr.unity)), np.pi / 2.0)
-    assert_almost_equal(pr.angle_between_vectors(
-        pr.unitz, pr.perpendicular_to_vector(pr.unitz)), np.pi / 2.0)
+    assert pytest.approx(pr.angle_between_vectors(
+        pr.unitx, pr.perpendicular_to_vector(pr.unitx))) == np.pi / 2.0
+    assert pytest.approx(pr.angle_between_vectors(
+        pr.unity, pr.perpendicular_to_vector(pr.unity))) == np.pi / 2.0
+    assert pytest.approx(pr.angle_between_vectors(
+        pr.unitz, pr.perpendicular_to_vector(pr.unitz))) == np.pi / 2.0
     random_state = np.random.RandomState(0)
     for _ in range(5):
         a = pr.norm_vector(pr.random_vector(random_state))
-        assert_almost_equal(pr.angle_between_vectors(
-            a, pr.perpendicular_to_vector(a)), np.pi / 2.0)
+        assert pytest.approx(pr.angle_between_vectors(
+            a, pr.perpendicular_to_vector(a))) == np.pi / 2.0
         b = a - np.array([a[0], 0.0, 0.0])
-        assert_almost_equal(pr.angle_between_vectors(
-            b, pr.perpendicular_to_vector(b)), np.pi / 2.0)
+        assert pytest.approx(pr.angle_between_vectors(
+            b, pr.perpendicular_to_vector(b))) == np.pi / 2.0
         c = a - np.array([0.0, a[1], 0.0])
-        assert_almost_equal(pr.angle_between_vectors(
-            c, pr.perpendicular_to_vector(c)), np.pi / 2.0)
+        assert pytest.approx(pr.angle_between_vectors(
+            c, pr.perpendicular_to_vector(c))) == np.pi / 2.0
         d = a - np.array([0.0, 0.0, a[2]])
-        assert_almost_equal(pr.angle_between_vectors(
-            d, pr.perpendicular_to_vector(d)), np.pi / 2.0)
+        assert pytest.approx(pr.angle_between_vectors(
+            d, pr.perpendicular_to_vector(d))) == np.pi / 2.0
 
 
 def test_angle_between_vectors():
@@ -132,17 +129,17 @@ def test_angle_between_vectors():
     a = np.array([0, 1, 0, np.pi / 2])
     R = pr.matrix_from_axis_angle(a)
     vR = np.dot(R, v)
-    assert_almost_equal(pr.angle_between_vectors(vR, v), a[-1])
+    assert pytest.approx(pr.angle_between_vectors(vR, v)) == a[-1]
     v = np.array([0, 1, 0])
     a = np.array([1, 0, 0, np.pi / 2])
     R = pr.matrix_from_axis_angle(a)
     vR = np.dot(R, v)
-    assert_almost_equal(pr.angle_between_vectors(vR, v), a[-1])
+    assert pytest.approx(pr.angle_between_vectors(vR, v)) == a[-1]
     v = np.array([0, 0, 1])
     a = np.array([1, 0, 0, np.pi / 2])
     R = pr.matrix_from_axis_angle(a)
     vR = np.dot(R, v)
-    assert_almost_equal(pr.angle_between_vectors(vR, v), a[-1])
+    assert pytest.approx(pr.angle_between_vectors(vR, v)) == a[-1]
 
 
 def test_angle_between_close_vectors():
@@ -153,7 +150,7 @@ def test_angle_between_close_vectors():
     a = np.array([0.9689124217106448, 0.24740395925452294, 0.0, 0.0])
     b = np.array([0.9689124217106448, 0.247403959254523, 0.0, 0.0])
     angle = pr.angle_between_vectors(a, b)
-    assert_almost_equal(angle, 0.0)
+    assert pytest.approx(angle) == 0.0
 
 
 def test_angle_to_zero_vector_is_nan():
@@ -162,8 +159,8 @@ def test_angle_to_zero_vector_is_nan():
     b = np.array([0.0, 0.0])
     with warnings.catch_warnings(record=True) as w:
         angle = pr.angle_between_vectors(a, b)
-        assert_equal(len(w), 1)
-    assert_true(np.isnan(angle))
+        assert len(w) == 1
+    assert np.isnan(angle)
 
 
 def test_vector_projection_on_zero_vector():
@@ -180,53 +177,52 @@ def test_vector_projection():
     a = np.ones(3)
     a_on_unitx = pr.vector_projection(a, pr.unitx)
     assert_array_almost_equal(a_on_unitx, pr.unitx)
-    assert_almost_equal(pr.angle_between_vectors(a_on_unitx, pr.unitx), 0.0)
+    assert pytest.approx(pr.angle_between_vectors(a_on_unitx, pr.unitx)) == 0.0
 
     a2_on_unitx = pr.vector_projection(2 * a, pr.unitx)
     assert_array_almost_equal(a2_on_unitx, 2 * pr.unitx)
-    assert_almost_equal(pr.angle_between_vectors(a2_on_unitx, pr.unitx), 0.0)
+    assert pytest.approx(pr.angle_between_vectors(a2_on_unitx, pr.unitx)) == 0.0
 
     a_on_unity = pr.vector_projection(a, pr.unity)
     assert_array_almost_equal(a_on_unity, pr.unity)
-    assert_almost_equal(pr.angle_between_vectors(a_on_unity, pr.unity), 0.0)
+    assert pytest.approx(pr.angle_between_vectors(a_on_unity, pr.unity)) == 0.0
 
     minus_a_on_unity = pr.vector_projection(-a, pr.unity)
     assert_array_almost_equal(minus_a_on_unity, -pr.unity)
-    assert_almost_equal(
-        pr.angle_between_vectors(minus_a_on_unity, pr.unity), np.pi)
+    assert pytest.approx(
+        pr.angle_between_vectors(minus_a_on_unity, pr.unity)) == np.pi
 
     a_on_unitz = pr.vector_projection(a, pr.unitz)
     assert_array_almost_equal(a_on_unitz, pr.unitz)
-    assert_almost_equal(pr.angle_between_vectors(a_on_unitz, pr.unitz), 0.0)
+    assert pytest.approx(pr.angle_between_vectors(a_on_unitz, pr.unitz)) == 0.0
 
     unitz_on_a = pr.vector_projection(pr.unitz, a)
     assert_array_almost_equal(unitz_on_a, np.ones(3) / 3.0)
-    assert_almost_equal(pr.angle_between_vectors(unitz_on_a, a), 0.0)
+    assert pytest.approx(pr.angle_between_vectors(unitz_on_a, a)) == 0.0
 
     unitx_on_unitx = pr.vector_projection(pr.unitx, pr.unitx)
     assert_array_almost_equal(unitx_on_unitx, pr.unitx)
-    assert_almost_equal(
-        pr.angle_between_vectors(unitx_on_unitx, pr.unitx), 0.0)
+    assert pytest.approx(
+        pr.angle_between_vectors(unitx_on_unitx, pr.unitx)) == 0.0
 
 
 def test_check_skew_symmetric_matrix():
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix with shape",
-        pr.check_skew_symmetric_matrix, [])
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix with shape",
-        pr.check_skew_symmetric_matrix, np.zeros((3, 4)))
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix with shape",
-        pr.check_skew_symmetric_matrix, np.zeros((4, 3)))
+    with pytest.raises(ValueError,
+                       match="Expected skew-symmetric matrix with shape"):
+        pr.check_skew_symmetric_matrix([])
+    with pytest.raises(ValueError,
+                       match="Expected skew-symmetric matrix with shape"):
+        pr.check_skew_symmetric_matrix(np.zeros((3, 4)))
+    with pytest.raises(ValueError,
+                       match="Expected skew-symmetric matrix with shape"):
+        pr.check_skew_symmetric_matrix(np.zeros((4, 3)))
     V = np.zeros((3, 3))
     V[0, 0] = 0.001
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix, but it failed the test",
-        pr.check_skew_symmetric_matrix, V)
+    with pytest.raises(ValueError, match="Expected skew-symmetric matrix, but it failed the test"):
+        pr.check_skew_symmetric_matrix(V)
     with warnings.catch_warnings(record=True) as w:
         pr.check_skew_symmetric_matrix(V, strict_check=False)
-        assert_equal(len(w), 1)
+        assert len(w) == 1
 
     pr.check_skew_symmetric_matrix(np.zeros((3, 3)))
 
@@ -248,33 +244,34 @@ def test_check_matrix():
     """Test input validation for rotation matrix."""
     R_list = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
     R = pr.check_matrix(R_list)
-    assert_equal(type(R), np.ndarray)
-    assert_equal(R.dtype, np.float64)
+    assert type(R) == np.ndarray
+    assert R.dtype == np.float64
 
     R_int_array = np.eye(3, dtype=int)
     R = pr.check_matrix(R_int_array)
-    assert_equal(type(R), np.ndarray)
-    assert_equal(R.dtype, np.float64)
+    assert type(R) == np.ndarray
+    assert R.dtype == np.float64
 
     R_array = np.eye(3)
     R = pr.check_matrix(R_array)
     assert_array_equal(R_array, R)
 
     R = np.eye(4)
-    assert_raises_regexp(
-        ValueError, "Expected rotation matrix with shape",
-        pr.check_matrix, R)
+    with pytest.raises(
+            ValueError, match="Expected rotation matrix with shape"):
+        pr.check_matrix(R)
 
     R = np.array([[1, 0, 0], [0, 1, 0], [0, 0.1, 1]])
-    assert_raises_regexp(
-        ValueError, "inversion by transposition", pr.check_matrix, R)
+    with pytest.raises(ValueError, match="inversion by transposition"):
+        pr.check_matrix(R)
 
     R = np.array([[1, 0, 1e-16], [0, 1, 0], [0, 0, 1]])
     R2 = pr.check_matrix(R)
     assert_array_equal(R, R2)
 
     R = -np.eye(3)
-    assert_raises_regexp(ValueError, "determinant", pr.check_matrix, R)
+    with pytest.raises(ValueError, match="determinant"):
+        pr.check_matrix(R)
 
 
 def test_check_axis_angle():
@@ -282,8 +279,8 @@ def test_check_axis_angle():
     a_list = [1, 0, 0, 0]
     a = pr.check_axis_angle(a_list)
     assert_array_almost_equal(a_list, a)
-    assert_equal(type(a), np.ndarray)
-    assert_equal(a.dtype, np.float64)
+    assert type(a) == np.ndarray
+    assert a.dtype == np.float64
 
     random_state = np.random.RandomState(0)
     a = np.empty(4)
@@ -291,16 +288,16 @@ def test_check_axis_angle():
     a[3] = random_state.randn() * 4.0 * np.pi
     a2 = pr.check_axis_angle(a)
     pr.assert_axis_angle_equal(a, a2)
-    assert_almost_equal(np.linalg.norm(a2[:3]), 1.0)
-    assert_greater(a2[3], 0)
-    assert_greater(np.pi, a2[3])
+    assert pytest.approx(np.linalg.norm(a2[:3])) == 1.0
+    assert a2[3] > 0
+    assert np.pi > a2[3]
 
-    assert_raises_regexp(
-        ValueError, "Expected axis and angle in array with shape",
-        pr.check_axis_angle, np.zeros(3))
-    assert_raises_regexp(
-        ValueError, "Expected axis and angle in array with shape",
-        pr.check_axis_angle, np.zeros((3, 3)))
+    with pytest.raises(
+            ValueError, match="Expected axis and angle in array with shape"):
+        pr.check_axis_angle(np.zeros(3))
+    with pytest.raises(
+            ValueError, match="Expected axis and angle in array with shape"):
+        pr.check_axis_angle(np.zeros((3, 3)))
 
 
 def test_check_compact_axis_angle():
@@ -308,23 +305,22 @@ def test_check_compact_axis_angle():
     a_list = [0, 0, 0]
     a = pr.check_compact_axis_angle(a_list)
     assert_array_almost_equal(a_list, a)
-    assert_equal(type(a), np.ndarray)
-    assert_equal(a.dtype, np.float64)
+    assert type(a) == np.ndarray
+    assert a.dtype == np.float64
 
     random_state = np.random.RandomState(0)
     a = pr.norm_vector(pr.random_vector(random_state, 3))
     a *= np.pi + random_state.randn() * 4.0 * np.pi
     a2 = pr.check_compact_axis_angle(a)
     pr.assert_compact_axis_angle_equal(a, a2)
-    assert_greater(np.linalg.norm(a2), 0)
-    assert_greater(np.pi, np.linalg.norm(a2))
+    assert np.pi > np.linalg.norm(a2) > 0
 
-    assert_raises_regexp(
-        ValueError, "Expected axis and angle in array with shape",
-        pr.check_compact_axis_angle, np.zeros(4))
-    assert_raises_regexp(
-        ValueError, "Expected axis and angle in array with shape",
-        pr.check_compact_axis_angle, np.zeros((3, 3)))
+    with pytest.raises(
+            ValueError, match="Expected axis and angle in array with shape"):
+        pr.check_compact_axis_angle(np.zeros(4))
+    with pytest.raises(
+            ValueError, match="Expected axis and angle in array with shape"):
+        pr.check_compact_axis_angle(np.zeros((3, 3)))
 
 
 def test_check_quaternion():
@@ -332,18 +328,18 @@ def test_check_quaternion():
     q_list = [1, 0, 0, 0]
     q = pr.check_quaternion(q_list)
     assert_array_almost_equal(q_list, q)
-    assert_equal(type(q), np.ndarray)
-    assert_equal(q.dtype, np.float64)
+    assert type(q) == np.ndarray
+    assert q.dtype == np.float64
 
     random_state = np.random.RandomState(0)
     q = random_state.randn(4)
     q = pr.check_quaternion(q)
-    assert_almost_equal(np.linalg.norm(q), 1.0)
+    assert pytest.approx(np.linalg.norm(q)) == 1.0
 
-    assert_raises_regexp(ValueError, "Expected quaternion with shape",
-                         pr.check_quaternion, np.zeros(3))
-    assert_raises_regexp(ValueError, "Expected quaternion with shape",
-                         pr.check_quaternion, np.zeros((3, 3)))
+    with pytest.raises(ValueError, match="Expected quaternion with shape"):
+         pr.check_quaternion(np.zeros(3))
+    with pytest.raises(ValueError, match="Expected quaternion with shape"):
+         pr.check_quaternion(np.zeros((3, 3)))
 
     q = np.array([0.0, 1.2, 0.0, 0.0])
     q2 = pr.check_quaternion(q, unit=False)
@@ -355,9 +351,9 @@ def test_check_quaternions():
     Q_list = [[1, 0, 0, 0]]
     Q = pr.check_quaternions(Q_list)
     assert_array_almost_equal(Q_list, Q)
-    assert_equal(type(Q), np.ndarray)
-    assert_equal(Q.dtype, np.float64)
-    assert_equal(Q.ndim, 2)
+    assert type(Q) == np.ndarray
+    assert Q.dtype == np.float64
+    assert Q.ndim == 2
     assert_array_equal(Q.shape, (1, 4))
 
     Q = np.array([
@@ -368,12 +364,12 @@ def test_check_quaternions():
     ])
     Q = pr.check_quaternions(Q)
     for i in range(len(Q)):
-        assert_almost_equal(np.linalg.norm(Q[i]), 1)
+        assert pytest.approx(np.linalg.norm(Q[i])) == 1
 
-    assert_raises_regexp(ValueError, "Expected quaternion array with shape",
-                         pr.check_quaternions, np.zeros(4))
-    assert_raises_regexp(ValueError, "Expected quaternion array with shape",
-                         pr.check_quaternions, np.zeros((3, 3)))
+    with pytest.raises(ValueError, match="Expected quaternion array with shape"):
+        pr.check_quaternions(np.zeros(4))
+    with pytest.raises(ValueError, match="Expected quaternion array with shape"):
+        pr.check_quaternions(np.zeros((3, 3)))
 
     Q = np.array([[0.0, 1.2, 0.0, 0.0]])
     Q2 = pr.check_quaternions(Q, unit=False)
@@ -382,10 +378,10 @@ def test_check_quaternions():
 
 def test_passive_matrix_from_angle():
     """Sanity checks for rotation around basis vectors."""
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.passive_matrix_from_angle, -1, 0)
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.passive_matrix_from_angle, 3, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+         pr.passive_matrix_from_angle(-1, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+        pr.passive_matrix_from_angle(3, 0)
 
     R = pr.passive_matrix_from_angle(0, -0.5 * np.pi)
     assert_array_almost_equal(R, np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]]))
@@ -405,10 +401,10 @@ def test_passive_matrix_from_angle():
 
 def test_active_matrix_from_angle():
     """Sanity checks for rotation around basis vectors."""
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.active_matrix_from_angle, -1, 0)
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.active_matrix_from_angle, 3, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+         pr.active_matrix_from_angle(-1, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+        pr.active_matrix_from_angle(3, 0)
 
     random_state = np.random.RandomState(21)
     for i in range(20):
@@ -610,18 +606,18 @@ def test_active_matrix_from_intrinsic_zyx():
         euler_zyx[1] = 0.5 * np.pi
         R = pr.active_matrix_from_intrinsic_euler_zyx(euler_zyx)
         euler_zyx2 = pr.intrinsic_euler_zyx_from_active_matrix(R)
-        assert_almost_equal(euler_zyx2[1], 0.5 * np.pi)
-        assert_almost_equal(
-            euler_zyx[0] - euler_zyx[2], euler_zyx2[0] - euler_zyx2[2])
+        assert pytest.approx(euler_zyx2[1]) == 0.5 * np.pi
+        assert (pytest.approx(euler_zyx[0] - euler_zyx[2])
+                == euler_zyx2[0] - euler_zyx2[2])
 
         # Gimbal lock 2, infinite solutions with constraint
         # alpha + gamma = constant
         euler_zyx[1] = -0.5 * np.pi
         R = pr.active_matrix_from_intrinsic_euler_zyx(euler_zyx)
         euler_zyx2 = pr.intrinsic_euler_zyx_from_active_matrix(R)
-        assert_almost_equal(euler_zyx2[1], -0.5 * np.pi)
-        assert_almost_equal(
-            euler_zyx[0] + euler_zyx[2], euler_zyx2[0] + euler_zyx2[2])
+        assert pytest.approx(euler_zyx2[1]) == -0.5 * np.pi
+        assert (pytest.approx(euler_zyx[0] + euler_zyx[2])
+                == euler_zyx2[0] + euler_zyx2[2])
 
 
 def test_active_matrix_from_extrinsic_zyx():
@@ -643,9 +639,9 @@ def test_active_matrix_from_extrinsic_zyx():
         euler_zyx[1] = 0.5 * np.pi
         R = pr.active_matrix_from_extrinsic_euler_zyx(euler_zyx)
         euler_zyx2 = pr.extrinsic_euler_zyx_from_active_matrix(R)
-        assert_almost_equal(euler_zyx2[1], 0.5 * np.pi)
-        assert_almost_equal(
-            euler_zyx[0] + euler_zyx[2], euler_zyx2[0] + euler_zyx2[2])
+        assert pytest.approx(euler_zyx2[1]) == 0.5 * np.pi
+        assert pytest.approx(
+            euler_zyx[0] + euler_zyx[2]) == euler_zyx2[0] + euler_zyx2[2]
         R2 = pr.active_matrix_from_extrinsic_euler_zyx(euler_zyx2)
         assert_array_almost_equal(R, R2)
 
@@ -654,9 +650,9 @@ def test_active_matrix_from_extrinsic_zyx():
         euler_zyx[1] = -0.5 * np.pi
         R = pr.active_matrix_from_extrinsic_euler_zyx(euler_zyx)
         euler_zyx2 = pr.extrinsic_euler_zyx_from_active_matrix(R)
-        assert_almost_equal(euler_zyx2[1], -0.5 * np.pi)
-        assert_almost_equal(
-            euler_zyx[0] - euler_zyx[2], euler_zyx2[0] - euler_zyx2[2])
+        assert pytest.approx(euler_zyx2[1]) == -0.5 * np.pi
+        assert pytest.approx(
+            euler_zyx[0] - euler_zyx[2]) == euler_zyx2[0] - euler_zyx2[2]
         R2 = pr.active_matrix_from_extrinsic_euler_zyx(euler_zyx2)
         assert_array_almost_equal(R, R2)
 
@@ -685,7 +681,7 @@ def _test_conversion_matrix_euler(
             euler[1] = 0.5 * np.pi
         R = matrix_from_euler(euler)
         euler2 = euler_from_matrix(R)
-        assert_almost_equal(euler[1], euler2[1])
+        assert pytest.approx(euler[1]) == euler2[1]
         R2 = matrix_from_euler(euler2)
         assert_array_almost_equal(R, R2)
 
@@ -696,7 +692,7 @@ def _test_conversion_matrix_euler(
             euler[1] = -0.5 * np.pi
         R = matrix_from_euler(euler)
         euler2 = euler_from_matrix(R)
-        assert_almost_equal(euler[1], euler2[1])
+        assert pytest.approx(euler[1]) == euler2[1]
         R2 = matrix_from_euler(euler2)
         assert_array_almost_equal(R, R2)
 
@@ -842,24 +838,30 @@ def test_active_matrix_from_extrinsic_roll_pitch_yaw():
 
 def test_from_quaternion():
     """Test conversion from quaternion to Euler angles."""
-    assert_raises_regexp(
-        ValueError, "Axis index i \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, -1, 0, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index i \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, 3, 0, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index j \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, 2, -1, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index j \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, 2, 3, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index k \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, 2, 0, -1, True)
-    assert_raises_regexp(
-        ValueError, "Axis index k \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.euler_from_quaternion, pr.q_id, 2, 0, 3, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index i \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, -1, 0, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index i \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, 3, 0, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index j \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, 2, -1, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index j \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, 2, 3, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index k \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, 2, 0, -1, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index k \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.euler_from_quaternion(pr.q_id, 2, 0, 3, True)
 
     random_state = np.random.RandomState(32)
 
@@ -1023,21 +1025,21 @@ def test_compare_axis_angle_from_matrix_to_lynch_park():
     pr.assert_axis_angle_equal(a, [0, 0, 0, 0])
 
     R = pr.passive_matrix_from_angle(2, np.pi)
-    assert_almost_equal(np.trace(R), -1)
+    assert pytest.approx(np.trace(R)) == -1
     a = pr.axis_angle_from_matrix(R)
     axis = (1.0 / np.sqrt(2.0 * (1 + R[2, 2]))
             * np.array([R[0, 2], R[1, 2], 1 + R[2, 2]]))
     pr.assert_axis_angle_equal(a, np.hstack((axis, (np.pi,))))
 
     R = pr.passive_matrix_from_angle(1, np.pi)
-    assert_almost_equal(np.trace(R), -1)
+    assert pytest.approx(np.trace(R)) == -1
     a = pr.axis_angle_from_matrix(R)
     axis = (1.0 / np.sqrt(2.0 * (1 + R[1, 1]))
             * np.array([R[0, 1], 1 + R[1, 1], R[2, 1]]))
     pr.assert_axis_angle_equal(a, np.hstack((axis, (np.pi,))))
 
     R = pr.passive_matrix_from_angle(0, np.pi)
-    assert_almost_equal(np.trace(R), -1)
+    assert pytest.approx(np.trace(R)) == -1
     a = pr.axis_angle_from_matrix(R)
     axis = (1.0 / np.sqrt(2.0 * (1 + R[0, 0]))
             * np.array([1 + R[0, 0], R[1, 0], R[2, 0]]))
@@ -1139,16 +1141,16 @@ def test_issue43_numerical_precision():
     R = pr.matrix_from_axis_angle(a)
     a2 = pr.axis_angle_from_matrix(R)
     axis_dist = np.linalg.norm(a[:3] - a2[:3])
-    assert_less(axis_dist, 1e-10)
-    assert_less(abs(a[3] - a2[3]), 1e-8)
+    assert axis_dist < 1e-10
+    assert abs(a[3] - a2[3]) < 1e-8
 
     a = np.array([1., 1., 1., 1e-7])
     a[:3] = a[:3] / np.linalg.norm(a[:3])
     R = pr.matrix_from_axis_angle(a)
     a2 = pr.axis_angle_from_matrix(R)
     axis_dist = np.linalg.norm(a[:3] - a2[:3])
-    assert_less(axis_dist, 1e-10)
-    assert_less(abs(a[3] - a2[3]), 1e-8)
+    assert axis_dist < 1e-10
+    assert abs(a[3] - a2[3]) < 1e-8
 
 
 def test_conversions_matrix_axis_angle_continuous():
@@ -1222,8 +1224,8 @@ def test_quaternion_from_matrix_180():
         [[0.0, 0.0, 0.0],
          [0.0, 0.0, 0.0],
          [0.0, 0.0, -1.0]])
-    assert_raises_regexp(
-        ValueError, "Expected rotation matrix", pr.quaternion_from_matrix, R)
+    with pytest.raises(ValueError, match="Expected rotation matrix"):
+        pr.quaternion_from_matrix(R)
 
     R = np.array(
         [[-1.0, 0.0, 0.0],
@@ -1308,8 +1310,8 @@ def test_axis_angle_from_two_direction_vectors():
         v1 = pr.random_vector(random_state, 3)
         v2 = R.dot(v1)
         a = pr.axis_angle_from_two_directions(v1, v2)
-        assert_almost_equal(pr.angle_between_vectors(v1, a[:3]), 0.5 * np.pi)
-        assert_almost_equal(pr.angle_between_vectors(v2, a[:3]), 0.5 * np.pi)
+        assert pytest.approx(pr.angle_between_vectors(v1, a[:3])) == 0.5 * np.pi
+        assert pytest.approx(pr.angle_between_vectors(v2, a[:3])) == 0.5 * np.pi
         assert_array_almost_equal(v2, pr.matrix_from_axis_angle(a).dot(v1))
 
 
@@ -1323,7 +1325,7 @@ def test_axis_angle_from_compact_axis_angle():
     for _ in range(5):
         ca = pr.random_compact_axis_angle(random_state)
         a = pr.axis_angle_from_compact_axis_angle(ca)
-        assert_almost_equal(np.linalg.norm(ca), a[3])
+        assert pytest.approx(np.linalg.norm(ca)) == a[3]
         assert_array_almost_equal(ca[:3] / np.linalg.norm(ca), a[:3])
 
 
@@ -1338,7 +1340,7 @@ def test_compact_axis_angle():
         a = pr.random_axis_angle(random_state)
         ca = pr.compact_axis_angle(a)
         assert_array_almost_equal(pr.norm_vector(ca), a[:3])
-        assert_almost_equal(np.linalg.norm(ca), a[3])
+        assert pytest.approx(np.linalg.norm(ca)) == a[3]
 
 
 def test_conversions_compact_axis_angle_quaternion():
@@ -1391,7 +1393,7 @@ def test_interpolate_same_axis_angle():
     random_state = np.random.RandomState(42)
     a = pr.random_axis_angle(random_state)
     traj = [pr.axis_angle_slerp(a, a, t) for t in np.linspace(0, 1, n_steps)]
-    assert_equal(len(traj), n_steps)
+    assert len(traj) == n_steps
     assert_array_almost_equal(traj[0], a)
     assert_array_almost_equal(traj[1], a)
     assert_array_almost_equal(traj[2], a)
@@ -1405,7 +1407,7 @@ def test_interpolate_almost_same_axis_angle():
     a2 = np.copy(a1)
     a2[-1] += np.finfo("float").eps
     traj = [pr.axis_angle_slerp(a1, a2, t) for t in np.linspace(0, 1, n_steps)]
-    assert_equal(len(traj), n_steps)
+    assert len(traj) == n_steps
     assert_array_almost_equal(traj[0], a1)
     assert_array_almost_equal(traj[1], a1)
     assert_array_almost_equal(traj[2], a2)
@@ -1452,7 +1454,7 @@ def test_interpolate_quaternion_shortest_path():
         [pr.quaternion_dist(r, s)
          for r, s in zip(traj_q_opposing[:-1], traj_q_opposing[1:])])
 
-    assert_greater(path_length_opposing, path_length)
+    assert path_length_opposing > path_length
 
     traj_q_opposing_corrected = [
         pr.quaternion_slerp(q1, q2, t, shortest_path=True)
@@ -1462,7 +1464,7 @@ def test_interpolate_quaternion_shortest_path():
          for r, s in zip(traj_q_opposing_corrected[:-1],
                          traj_q_opposing_corrected[1:])])
 
-    assert_almost_equal(path_length_opposing_corrected, path_length)
+    assert pytest.approx(path_length_opposing_corrected) == path_length
 
 
 def test_interpolate_same_quaternion():
@@ -1475,7 +1477,7 @@ def test_interpolate_same_quaternion():
     a = pr.random_axis_angle(random_state)
     q = pr.quaternion_from_axis_angle(a)
     traj = [pr.quaternion_slerp(q, q, t) for t in np.linspace(0, 1, n_steps)]
-    assert_equal(len(traj), n_steps)
+    assert len(traj) == n_steps
     assert_array_almost_equal(traj[0], q)
     assert_array_almost_equal(traj[1], q)
     assert_array_almost_equal(traj[2], q)
@@ -1513,7 +1515,7 @@ def test_quaternion_conventions():
     q_wxyz_random = pr.random_quaternion(random_state)
     q_xyzw_random = pr.quaternion_xyzw_from_wxyz(q_wxyz_random)
     assert_array_equal(q_xyzw_random[:3], q_wxyz_random[1:])
-    assert_equal(q_xyzw_random[3], q_wxyz_random[0])
+    assert q_xyzw_random[3] == q_wxyz_random[0]
     q_wxyz_random2 = pr.quaternion_wxyz_from_xyzw(q_xyzw_random)
     assert_array_equal(q_wxyz_random, q_wxyz_random2)
 
@@ -1618,13 +1620,13 @@ def test_quaternion_dist():
         q1 = pr.quaternion_from_axis_angle(pr.random_axis_angle(random_state))
         q2 = pr.quaternion_from_axis_angle(pr.random_axis_angle(random_state))
         q1_to_q1 = pr.quaternion_dist(q1, q1)
-        assert_almost_equal(q1_to_q1, 0.0)
+        assert pytest.approx(q1_to_q1) == 0.0
         q2_to_q2 = pr.quaternion_dist(q2, q2)
-        assert_almost_equal(q2_to_q2, 0.0)
+        assert pytest.approx(q2_to_q2) == 0.0
         q1_to_q2 = pr.quaternion_dist(q1, q2)
         q2_to_q1 = pr.quaternion_dist(q2, q1)
-        assert_almost_equal(q1_to_q2, q2_to_q1)
-        assert_greater(2.0 * np.pi, q1_to_q2)
+        assert pytest.approx(q1_to_q2) == q2_to_q1
+        assert 2.0 * np.pi > q1_to_q2
 
 
 def test_quaternion_dist_for_identical_rotations():
@@ -1635,7 +1637,7 @@ def test_quaternion_dist_for_identical_rotations():
         q = pr.quaternion_from_axis_angle(pr.random_axis_angle(random_state))
         assert_array_almost_equal(pr.matrix_from_quaternion(q),
                                   pr.matrix_from_quaternion(-q))
-        assert_equal(pr.quaternion_dist(q, -q), 0.0)
+        assert pr.quaternion_dist(q, -q) == 0.0
 
 
 def test_quaternion_dist_for_almost_identical_rotations():
@@ -1647,7 +1649,7 @@ def test_quaternion_dist_for_almost_identical_rotations():
         q1 = pr.quaternion_from_axis_angle(a)
         r = 1e-4 * random_state.randn(4)
         q2 = -pr.quaternion_from_axis_angle(a + r)
-        assert_almost_equal(pr.quaternion_dist(q1, q2), 0.0, places=3)
+        assert pytest.approx(pr.quaternion_dist(q1, q2), abs=1e-3) == 0.0
 
 
 def test_quaternion_diff():
@@ -1695,17 +1697,18 @@ def test_asssert_rotation_matrix_behaves_like_check_matrix():
                 pr.assert_rotation_matrix(R)
                 pr.check_matrix(R)
             except AssertionError:
-                assert_raises_regexp(
-                    ValueError, "Expected rotation matrix", pr.check_matrix, R)
+                with pytest.raises(
+                        ValueError, match="Expected rotation matrix"):
+                    pr.check_matrix(R)
 
 
 def test_deactivate_rotation_matrix_precision_error():
     R = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 2.0]])
-    assert_raises_regexp(
-        ValueError, "Expected rotation matrix", pr.check_matrix, R)
+    with pytest.raises(ValueError, match="Expected rotation matrix"):
+        pr.check_matrix(R)
     with warnings.catch_warnings(record=True) as w:
         pr.check_matrix(R, strict_check=False)
-        assert_equal(len(w), 2)
+        assert len(w) == 2
 
 
 def test_norm_rotation_matrix():
@@ -1715,25 +1718,22 @@ def test_norm_rotation_matrix():
     R[1, 0] += np.finfo(float).eps
     R = pr.norm_matrix(R)
     assert_array_equal(R, np.eye(3))
-    assert_equal(np.linalg.det(R), 1.0)
+    assert np.linalg.det(R) == 1.0
 
     R = np.eye(3)
     R[1, 1] += 0.3
     R_norm = pr.norm_matrix(R)
-    assert_almost_equal(np.linalg.det(R_norm), 1.0)
+    assert pytest.approx(np.linalg.det(R_norm)) == 1.0
     assert_array_almost_equal(np.eye(3), R_norm)
 
 
 def test_matrix_from_two_vectors():
-    assert_raises_regexp(
-        ValueError, "a must not be the zero vector",
-        pr.matrix_from_two_vectors, np.zeros(3), np.zeros(3))
-    assert_raises_regexp(
-        ValueError, "b must not be the zero vector",
-        pr.matrix_from_two_vectors, np.ones(3), np.zeros(3))
-    assert_raises_regexp(
-        ValueError, "a and b must not be parallel",
-        pr.matrix_from_two_vectors, np.ones(3), np.ones(3))
+    with pytest.raises(ValueError, match="a must not be the zero vector"):
+        pr.matrix_from_two_vectors(np.zeros(3), np.zeros(3))
+    with pytest.raises(ValueError, match="b must not be the zero vector"):
+        pr.matrix_from_two_vectors(np.ones(3), np.zeros(3))
+    with pytest.raises(ValueError, match="a and b must not be parallel"):
+        pr.matrix_from_two_vectors(np.ones(3), np.ones(3))
 
     R = pr.matrix_from_two_vectors(pr.unitx, pr.unity)
     assert_array_almost_equal(R, np.eye(3))
@@ -1745,7 +1745,8 @@ def test_matrix_from_two_vectors():
         R = pr.matrix_from_two_vectors(a, b)
         pr.assert_rotation_matrix(R)
         assert_array_almost_equal(pr.norm_vector(a), R[:, 0])
-        assert_almost_equal(pr.angle_between_vectors(b, R[:, 2]), 0.5 * np.pi)
+        assert pytest.approx(
+            pr.angle_between_vectors(b, R[:, 2])) == 0.5 * np.pi
 
 
 def test_axis_angle_from_matrix_cos_angle_greater_1():
@@ -1755,32 +1756,32 @@ def test_axis_angle_from_matrix_cos_angle_greater_1():
         [-2.3816502529500374e-08, -1.2457848247850049e-08, 0.9999999999999999]
     ])
     a = pr.axis_angle_from_matrix(R)
-    assert_false(any(np.isnan(a)))
+    assert not any(np.isnan(a))
 
 
 def test_axis_angle_from_matrix_without_check():
     R = -np.eye(3)
     with warnings.catch_warnings(record=True) as w:
         a = pr.axis_angle_from_matrix(R, check=False)
-    assert_equal(len(w), 1)
-    assert_true(all(np.isnan(a[:3])))
+    assert len(w) == 1
+    assert all(np.isnan(a[:3]))
 
 
 def test_check_rotor():
     r_list = [1, 0, 0, 0]
     r = pr.check_rotor(r_list)
-    assert_true(isinstance(r, np.ndarray))
+    assert isinstance(r, np.ndarray)
 
     r2 = np.array([[1, 0, 0, 0]])
-    assert_raises_regexp(
-        ValueError, "Expected rotor with shape", pr.check_rotor, r2)
+    with pytest.raises(ValueError, match="Expected rotor with shape"):
+        pr.check_rotor(r2)
 
     r3 = np.array([1, 0, 0])
-    assert_raises_regexp(
-        ValueError, "Expected rotor with shape", pr.check_rotor, r3)
+    with pytest.raises(ValueError, match="Expected rotor with shape"):
+        pr.check_rotor(r3)
 
     r4 = np.array([2, 0, 0, 0])
-    assert_almost_equal(np.linalg.norm(pr.check_rotor(r4)), 1.0)
+    assert pytest.approx(np.linalg.norm(pr.check_rotor(r4))) == 1.0
 
 
 def test_outer():
@@ -1831,15 +1832,14 @@ def test_geometric_product_creates_rotor_that_rotates_by_double_angle():
         b_unit = pr.norm_vector(random_state.randn(3))
         # a geometric product of two unit vectors is a rotor
         ab = pr.geometric_product(a_unit, b_unit)
-        assert_almost_equal(np.linalg.norm(ab), 1.0)
+        assert pytest.approx(np.linalg.norm(ab)) == 1.0
 
         angle = pr.angle_between_vectors(a_unit, b_unit)
         c = pr.rotor_apply(ab, a_unit)
         double_angle = pr.angle_between_vectors(a_unit, c)
 
-        assert_almost_equal(
-            abs(pr.norm_angle(2.0 * angle)),
-            abs(pr.norm_angle(double_angle)))
+        assert (pytest.approx(abs(pr.norm_angle(2.0 * angle)))
+                == abs(pr.norm_angle(double_angle)))
 
 
 def test_rotor_from_two_directions_special_cases():
@@ -2033,10 +2033,10 @@ def test_bug_198():
 
 def test_quaternion_from_angle():
     """Quaternion from rotation around basis vectors."""
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.quaternion_from_angle, -1, 0)
-    assert_raises_regexp(ValueError, "Basis must be in",
-                         pr.quaternion_from_angle, 3, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+        pr.quaternion_from_angle(-1, 0)
+    with pytest.raises(ValueError, match="Basis must be in"):
+        pr.quaternion_from_angle(3, 0)
 
     random_state = np.random.RandomState(22)
     for i in range(20):
@@ -2050,24 +2050,30 @@ def test_quaternion_from_angle():
 
 def test_quaternion_from_euler():
     """Quaternion from Euler angles."""
-    assert_raises_regexp(
-        ValueError, "Axis index i \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), -1, 0, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index i \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), 3, 0, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index j \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), 2, -1, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index j \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), 2, 3, 2, True)
-    assert_raises_regexp(
-        ValueError, "Axis index k \\(-1\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), 2, 0, -1, True)
-    assert_raises_regexp(
-        ValueError, "Axis index k \\(3\\) must be in \\[0, 1, 2\\]",
-        pr.quaternion_from_euler, np.zeros(3), 2, 0, 3, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index i \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), -1, 0, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index i \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), 3, 0, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index j \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), 2, -1, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index j \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), 2, 3, 2, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index k \\(-1\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), 2, 0, -1, True)
+    with pytest.raises(
+            ValueError,
+            match="Axis index k \\(3\\) must be in \\[0, 1, 2\\]"):
+        pr.quaternion_from_euler(np.zeros(3), 2, 0, 3, True)
 
     euler_axes = [
         [0, 2, 0],
diff --git a/pytransform3d/test/test_transform_manager.py b/pytransform3d/test/test_transform_manager.py
index 2692cc94f..86ae309c0 100644
--- a/pytransform3d/test/test_transform_manager.py
+++ b/pytransform3d/test/test_transform_manager.py
@@ -12,9 +12,7 @@
 from pytransform3d.transform_manager import TransformManager
 from pytransform3d import transform_manager
 from numpy.testing import assert_array_almost_equal
-from nose.tools import (assert_raises_regexp, assert_equal, assert_true,
-                        assert_false)
-from nose import SkipTest
+import pytest
 
 
 def test_request_added_transform():
@@ -47,9 +45,9 @@ def test_has_frame():
     """Check if frames have been registered with transform."""
     tm = TransformManager()
     tm.add_transform("A", "B", np.eye(4))
-    assert_true(tm.has_frame("A"))
-    assert_true(tm.has_frame("B"))
-    assert_false(tm.has_frame("C"))
+    assert tm.has_frame("A")
+    assert tm.has_frame("B")
+    assert not tm.has_frame("C")
 
 
 def test_transform_not_added():
@@ -62,10 +60,12 @@ def test_transform_not_added():
     tm.add_transform("A", "B", A2B)
     tm.add_transform("C", "D", C2D)
 
-    assert_raises_regexp(KeyError, "Unknown frame", tm.get_transform, "A", "G")
-    assert_raises_regexp(KeyError, "Unknown frame", tm.get_transform, "G", "D")
-    assert_raises_regexp(KeyError, "Cannot compute path", tm.get_transform,
-                         "A", "D")
+    with pytest.raises(KeyError, match="Unknown frame"):
+        tm.get_transform("A", "G")
+    with pytest.raises(KeyError, match="Unknown frame"):
+        tm.get_transform("G", "D")
+    with pytest.raises(KeyError, match="Cannot compute path"):
+        tm.get_transform("A", "D")
 
 
 def test_request_concatenated_transform():
@@ -105,8 +105,8 @@ def test_update_transform():
 
     # Hack: test depends on internal member
     assert_array_almost_equal(A2B, A2B2)
-    assert_equal(len(tm.i), 1)
-    assert_equal(len(tm.j), 1)
+    assert len(tm.i) == 1
+    assert len(tm.j) == 1
 
 
 def test_pickle():
@@ -138,10 +138,11 @@ def test_whitelist():
     tm.add_transform("A", "B", A2B)
 
     nodes = tm._whitelisted_nodes(None)
-    assert_equal({"A", "B"}, nodes)
+    assert {"A", "B"} == nodes
     nodes = tm._whitelisted_nodes(["A"])
-    assert_equal({"A"}, nodes)
-    assert_raises_regexp(KeyError, "unknown nodes", tm._whitelisted_nodes, "C")
+    assert {"A"} == nodes
+    with pytest.raises(KeyError, match="unknown nodes"):
+        tm._whitelisted_nodes(["C"])
 
 
 def test_check_consistency():
@@ -154,43 +155,43 @@ def test_check_consistency():
     tm.add_transform("A", "B", A2B)
     B2A = random_transform(random_state)
     tm.add_transform("B", "A", B2A)
-    assert_false(tm.check_consistency())
+    assert not tm.check_consistency()
 
     tm = TransformManager()
 
     A2B = random_transform(random_state)
     tm.add_transform("A", "B", A2B)
-    assert_true(tm.check_consistency())
+    assert tm.check_consistency()
 
     C2D = random_transform(random_state)
     tm.add_transform("C", "D", C2D)
-    assert_true(tm.check_consistency())
+    assert tm.check_consistency()
 
     B2C = random_transform(random_state)
     tm.add_transform("B", "C", B2C)
-    assert_true(tm.check_consistency())
+    assert tm.check_consistency()
 
     A2D_over_path = tm.get_transform("A", "D")
 
     A2D = random_transform(random_state)
     tm.add_transform("A", "D", A2D)
-    assert_false(tm.check_consistency())
+    assert not tm.check_consistency()
 
     tm.add_transform("A", "D", A2D_over_path)
-    assert_true(tm.check_consistency())
+    assert tm.check_consistency()
 
 
 def test_connected_components():
     """Test computation of connected components in the graph."""
     tm = TransformManager()
     tm.add_transform("A", "B", np.eye(4))
-    assert_equal(tm.connected_components(), 1)
+    assert tm.connected_components() == 1
     tm.add_transform("D", "E", np.eye(4))
-    assert_equal(tm.connected_components(), 2)
+    assert tm.connected_components() == 2
     tm.add_transform("B", "C", np.eye(4))
-    assert_equal(tm.connected_components(), 2)
+    assert tm.connected_components() == 2
     tm.add_transform("D", "C", np.eye(4))
-    assert_equal(tm.connected_components(), 1)
+    assert tm.connected_components() == 1
 
 
 def test_png_export():
@@ -214,9 +215,9 @@ def test_png_export():
     _, filename = tempfile.mkstemp(".png")
     try:
         tm.write_png(filename)
-        assert_true(os.path.exists(filename))
+        assert os.path.exists(filename)
     except ImportError:
-        raise SkipTest("pydot is required for this test")
+        pytest.skip("pydot is required for this test")
     finally:
         if os.path.exists(filename):
             try:
@@ -231,9 +232,10 @@ def test_png_export_without_pydot_fails():
     tm = TransformManager()
     try:
         transform_manager.PYDOT_AVAILABLE = False
-        assert_raises_regexp(
-            ImportError, "pydot must be installed to use this feature.",
-            tm.write_png, "bla")
+        with pytest.raises(
+                ImportError,
+                match="pydot must be installed to use this feature."):
+            tm.write_png("bla")
     finally:
         transform_manager.PYDOT_AVAILABLE = pydot_available
 
@@ -243,9 +245,8 @@ def test_deactivate_transform_manager_precision_error():
     A2B[0, 0] = 2.0
     A2B[3, 0] = 3.0
     tm = TransformManager()
-    assert_raises_regexp(
-        ValueError, "Expected rotation matrix",
-        tm.add_transform, "A", "B", A2B)
+    with pytest.raises(ValueError, match="Expected rotation matrix"):
+        tm.add_transform("A", "B", A2B)
 
     if int(platform.python_version()[0]) == 2:
         # Python 2 seems to incorrectly suppress some warnings, not sure why
@@ -259,7 +260,7 @@ def test_deactivate_transform_manager_precision_error():
             tm.add_transform("A", "B", A2B)
             tm.add_transform("B", "C", np.eye(4))
             tm.get_transform("C", "A")
-            assert_equal(len(w), n_expected_warnings)
+            assert len(w) == n_expected_warnings
     finally:
         warnings.filterwarnings("default", category=UserWarning)
 
@@ -277,16 +278,16 @@ def test_remove_transform():
     tm.add_transform("A", "B", np.eye(4))
     tm.add_transform("C", "D", np.eye(4))
 
-    assert_raises_regexp(
-        KeyError, "Cannot compute path", tm.get_transform, "A", "D")
+    with pytest.raises(KeyError, match="Cannot compute path"):
+        tm.get_transform("A", "D")
 
     tm.add_transform("B", "C", np.eye(4))
     tm.get_transform("A", "C")
 
     tm.remove_transform("B", "C")
     tm.remove_transform("B", "C")  # nothing should happen
-    assert_raises_regexp(
-        KeyError, "Cannot compute path", tm.get_transform, "A", "D")
+    with pytest.raises(KeyError, match="Cannot compute path"):
+        tm.get_transform("A", "D")
     tm.get_transform("B", "A")
     tm.get_transform("D", "C")
 
diff --git a/pytransform3d/test/test_transformations.py b/pytransform3d/test/test_transformations.py
index 24d77782f..7e75c7e3e 100644
--- a/pytransform3d/test/test_transformations.py
+++ b/pytransform3d/test/test_transformations.py
@@ -1,6 +1,8 @@
 import warnings
 import platform
 import numpy as np
+import pytest
+
 from pytransform3d.transformations import (
     random_transform, transform_from, translate_transform, rotate_transform,
     invert_transform, vector_to_point, vectors_to_points, vector_to_direction,
@@ -30,30 +32,30 @@
     axis_angle_from_matrix, norm_vector, perpendicular_to_vector,
     active_matrix_from_angle,
     random_quaternion, axis_angle_from_quaternion, norm_axis_angle)
-from nose.tools import (assert_equal, assert_almost_equal,
-                        assert_raises_regexp, assert_false, assert_true)
 from numpy.testing import assert_array_almost_equal
 
 
 def test_check_transform():
     """Test input validation for transformation matrix."""
     A2B = np.eye(3)
-    assert_raises_regexp(
-        ValueError, "Expected homogeneous transformation matrix with shape",
-        check_transform, A2B)
+    with pytest.raises(
+            ValueError,
+            match="Expected homogeneous transformation matrix with shape"):
+        check_transform(A2B)
 
     A2B = np.eye(4, dtype=int)
     A2B = check_transform(A2B)
-    assert_equal(type(A2B), np.ndarray)
-    assert_equal(A2B.dtype, np.float64)
+    assert type(A2B) == np.ndarray
+    assert A2B.dtype == np.float64
 
     A2B[:3, :3] = np.array([[1, 1, 1], [0, 0, 0], [2, 2, 2]])
-    assert_raises_regexp(ValueError, "rotation matrix", check_transform, A2B)
+    with pytest.raises(ValueError, match="rotation matrix"):
+        check_transform(A2B)
 
     A2B = np.eye(4)
     A2B[3, :] = np.array([0.1, 0.0, 0.0, 1.0])
-    assert_raises_regexp(ValueError, "homogeneous transformation matrix",
-                         check_transform, A2B)
+    with pytest.raises(ValueError, match="homogeneous transformation matrix"):
+        check_transform(A2B)
 
     random_state = np.random.RandomState(0)
     A2B = random_transform(random_state)
@@ -63,16 +65,14 @@ def test_check_transform():
 
 def test_translate_transform_with_check():
     A2B_broken = np.zeros((4, 4))
-    assert_raises_regexp(
-        ValueError, "rotation matrix", translate_transform,
-        A2B_broken, np.zeros(3))
+    with pytest.raises(ValueError, match="rotation matrix"):
+        translate_transform(A2B_broken, np.zeros(3))
 
 
 def test_rotate_transform_with_check():
     A2B_broken = np.zeros((4, 4))
-    assert_raises_regexp(
-        ValueError, "rotation matrix", rotate_transform,
-        A2B_broken, np.eye(3))
+    with pytest.raises(ValueError, match="rotation matrix"):
+        rotate_transform(A2B_broken, np.eye(3))
 
 
 def test_check_pq():
@@ -84,14 +84,16 @@ def test_check_pq():
     q3 = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]
     q4 = check_pq(q3)
     assert_array_almost_equal(q3, q4)
-    assert_equal(len(q3), q4.shape[0])
+    assert len(q3) == q4.shape[0]
 
     A2B = np.eye(4)
-    assert_raises_regexp(ValueError, "position and orientation quaternion",
-                         check_pq, A2B)
+    with pytest.raises(ValueError,
+                       match="position and orientation quaternion"):
+        check_pq(A2B)
     q5 = np.zeros(8)
-    assert_raises_regexp(ValueError, "position and orientation quaternion",
-                         check_pq, q5)
+    with pytest.raises(
+            ValueError, match="position and orientation quaternion"):
+        check_pq(q5)
 
 
 def test_invert_transform():
@@ -208,9 +210,10 @@ def test_transform():
     p1B = transform(A2B, PA[1])
     assert_array_almost_equal(PB, np.array([p0B, p1B]))
 
-    assert_raises_regexp(
-        ValueError, "Cannot transform array with more than 2 dimensions",
-        transform, A2B, np.zeros((2, 2, 4)))
+    with pytest.raises(
+            ValueError,
+            match="Cannot transform array with more than 2 dimensions"):
+        transform(A2B, np.zeros((2, 2, 4)))
 
 
 def test_scale_transform():
@@ -258,8 +261,8 @@ def test_deactivate_transform_precision_error():
     A2B = np.eye(4)
     A2B[0, 0] = 2.0
     A2B[3, 0] = 3.0
-    assert_raises_regexp(
-        ValueError, "Expected rotation matrix", check_transform, A2B)
+    with pytest.raises(ValueError, match="Expected rotation matrix"):
+        check_transform(A2B)
 
     if int(platform.python_version()[0]) == 2:
         # Python 2 seems to incorrectly suppress some warnings, not sure why
@@ -270,7 +273,7 @@ def test_deactivate_transform_precision_error():
         warnings.filterwarnings("always", category=UserWarning)
         with warnings.catch_warnings(record=True) as w:
             check_transform(A2B, strict_check=False)
-            assert_equal(len(w), n_expected_warnings)
+            assert len(w) == n_expected_warnings
     finally:
         warnings.filterwarnings("default", category=UserWarning)
 
@@ -302,7 +305,7 @@ def test_norm_exponential_coordinates():
         # ensure that theta is not within [-pi, pi]
         Stheta[random_state.randint(0, 3)] += np.pi + random_state.rand()
         Stheta_norm = norm_exponential_coordinates(Stheta)
-        assert_false(np.all(Stheta == Stheta_norm))
+        assert not np.all(Stheta == Stheta_norm)
 
         A2B = transform_from_exponential_coordinates(Stheta)
         Stheta2 = exponential_coordinates_from_transform(A2B)
@@ -314,30 +317,27 @@ def test_check_screw_parameters():
     s_axis = norm_vector(np.array([-1.0, 2.0, 3.0])).tolist()
     h = 0.2
 
-    assert_raises_regexp(
-        ValueError, "Expected 3D vector with shape",
-        check_screw_parameters, [0.0], s_axis, h)
-    assert_raises_regexp(
-        ValueError, "Expected 3D vector with shape",
-        check_screw_parameters, q, [0.0], h)
-    assert_raises_regexp(
-        ValueError, "s_axis must not have norm 0",
-        check_screw_parameters, q, np.zeros(3), h)
+    with pytest.raises(ValueError, match="Expected 3D vector with shape"):
+        check_screw_parameters([0.0], s_axis, h)
+    with pytest.raises(ValueError, match="Expected 3D vector with shape"):
+        check_screw_parameters(q, [0.0], h)
+    with pytest.raises(ValueError, match="s_axis must not have norm 0"):
+        check_screw_parameters(q, np.zeros(3), h)
 
     q2, s_axis2, h2 = check_screw_parameters(q, 2.0 * np.array(s_axis), h)
     assert_array_almost_equal(q, q2)
-    assert_almost_equal(h, h2)
-    assert_almost_equal(np.linalg.norm(s_axis2), 1.0)
+    assert pytest.approx(h) == h2
+    assert pytest.approx(np.linalg.norm(s_axis2)) == 1.0
 
     q2, s_axis2, h2 = check_screw_parameters(q, s_axis, h)
     assert_array_almost_equal(q, q2)
     assert_array_almost_equal(s_axis, s_axis2)
-    assert_almost_equal(h, h2)
+    assert pytest.approx(h) == h2
 
     q2, s_axis2, h2 = check_screw_parameters(q, s_axis, np.inf)
     assert_array_almost_equal(np.zeros(3), q2)
     assert_array_almost_equal(s_axis, s_axis2)
-    assert_almost_equal(np.inf, h2)
+    assert np.isinf(h2)
 
 
 def test_check_screw_axis():
@@ -345,17 +345,16 @@ def test_check_screw_axis():
     omega = norm_vector(random_vector(random_state, 3))
     v = random_vector(random_state, 3)
 
-    assert_raises_regexp(
-        ValueError, "Expected 3D vector with shape",
-        check_screw_axis, np.r_[0, 1, v])
+    with pytest.raises(ValueError, match="Expected 3D vector with shape"):
+        check_screw_axis(np.r_[0, 1, v])
 
-    assert_raises_regexp(
-        ValueError, "Norm of rotation axis must either be 0 or 1",
-        check_screw_axis, np.r_[2 * omega, v])
+    with pytest.raises(
+            ValueError, match="Norm of rotation axis must either be 0 or 1"):
+        check_screw_axis(np.r_[2 * omega, v])
 
-    assert_raises_regexp(
-        ValueError, "If the norm of the rotation axis is 0",
-        check_screw_axis, np.r_[0, 0, 0, v])
+    with pytest.raises(
+            ValueError, match="If the norm of the rotation axis is 0"):
+        check_screw_axis(np.r_[0, 0, 0, v])
 
     S_pure_translation = np.r_[0, 0, 0, norm_vector(v)]
     S = check_screw_axis(S_pure_translation)
@@ -367,9 +366,8 @@ def test_check_screw_axis():
 
 
 def test_check_exponential_coordinates():
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape",
-        check_exponential_coordinates, [0])
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_exponential_coordinates([0])
 
     Stheta = [0.0, 1.0, 2.0, -5.0, -2, 3]
     Stheta2 = check_exponential_coordinates(Stheta)
@@ -377,31 +375,28 @@ def test_check_exponential_coordinates():
 
 
 def test_check_screw_matrix():
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_screw_matrix,
-        np.zeros((1, 4, 4)))
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_screw_matrix,
-        np.zeros((3, 4)))
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_screw_matrix,
-        np.zeros((4, 3)))
-
-    assert_raises_regexp(
-        ValueError, "Last row of screw matrix must only contains zeros",
-        check_screw_matrix, np.eye(4))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_screw_matrix(np.zeros((1, 4, 4)))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_screw_matrix(np.zeros((3, 4)))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_screw_matrix(np.zeros((4, 3)))
+
+    with pytest.raises(ValueError, match="Last row of screw matrix must only "
+                                         "contains zeros"):
+        check_screw_matrix(np.eye(4))
 
     screw_matrix = screw_matrix_from_screw_axis(
         np.array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])) * 1.1
-    assert_raises_regexp(
-        ValueError, "Norm of rotation axis must either be 0 or 1",
-        check_screw_matrix, screw_matrix)
+    with pytest.raises(
+            ValueError, match="Norm of rotation axis must either be 0 or 1"):
+        check_screw_matrix(screw_matrix)
 
     screw_matrix = screw_matrix_from_screw_axis(
         np.array([0.0, 0.0, 0.0, 1.0, 0.0, 0.0])) * 1.1
-    assert_raises_regexp(
-        ValueError, "If the norm of the rotation axis is 0",
-        check_screw_matrix, screw_matrix)
+    with pytest.raises(
+            ValueError, match="If the norm of the rotation axis is 0"):
+        check_screw_matrix(screw_matrix)
 
     screw_matrix = screw_matrix_from_screw_axis(
         np.array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]))
@@ -416,26 +411,22 @@ def test_check_screw_matrix():
     screw_matrix = screw_matrix_from_screw_axis(
         np.array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]))
     screw_matrix[0, 0] = 0.0001
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix",
-        check_screw_matrix, screw_matrix)
+    with pytest.raises(ValueError, match="Expected skew-symmetric matrix"):
+        check_screw_matrix(screw_matrix)
 
 
 def test_check_transform_log():
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_transform_log,
-        np.zeros((1, 4, 4)))
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_transform_log,
-        np.zeros((3, 4)))
-    assert_raises_regexp(
-        ValueError, "Expected array-like with shape", check_transform_log,
-        np.zeros((4, 3)))
-
-    assert_raises_regexp(
-        ValueError, "Last row of logarithm of transformation must only "
-                    "contains zeros",
-        check_transform_log, np.eye(4))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_transform_log(np.zeros((1, 4, 4)))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_transform_log(np.zeros((3, 4)))
+    with pytest.raises(ValueError, match="Expected array-like with shape"):
+        check_transform_log(np.zeros((4, 3)))
+
+    with pytest.raises(
+            ValueError, match="Last row of logarithm of transformation must "
+                              "only contains zeros"):
+        check_transform_log(np.eye(4))
     transform_log = screw_matrix_from_screw_axis(
         np.array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])) * 1.1
     transform_log2 = check_transform_log(transform_log)
@@ -444,9 +435,8 @@ def test_check_transform_log():
     transform_log = screw_matrix_from_screw_axis(
         np.array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])) * 1.1
     transform_log[0, 0] = 0.0001
-    assert_raises_regexp(
-        ValueError, "Expected skew-symmetric matrix",
-        check_transform_log, transform_log)
+    with pytest.raises(ValueError, match="Expected skew-symmetric matrix"):
+        check_transform_log(transform_log)
 
 
 def test_random_screw_axis():
@@ -517,13 +507,13 @@ def test_conversions_between_screw_axis_and_exponential_coordinates():
     Stheta = exponential_coordinates_from_screw_axis(S, theta)
     S2, theta2 = screw_axis_from_exponential_coordinates(Stheta)
     assert_array_almost_equal(S, S2)
-    assert_almost_equal(theta, theta2)
+    assert pytest.approx(theta) == theta2
 
     S = np.zeros(6)
     theta = 0.0
     S2, theta2 = screw_axis_from_exponential_coordinates(np.zeros(6))
     assert_array_almost_equal(S, S2)
-    assert_almost_equal(theta, theta2)
+    assert pytest.approx(theta) == theta2
 
     random_state = np.random.RandomState(33)
     for _ in range(5):
@@ -532,7 +522,7 @@ def test_conversions_between_screw_axis_and_exponential_coordinates():
         Stheta = exponential_coordinates_from_screw_axis(S, theta)
         S2, theta2 = screw_axis_from_exponential_coordinates(Stheta)
         assert_array_almost_equal(S, S2)
-        assert_almost_equal(theta, theta2)
+        assert pytest.approx(theta) == theta2
 
 
 def test_conversions_between_exponential_coordinates_and_transform_log():
@@ -562,14 +552,14 @@ def test_conversions_between_screw_matrix_and_transform_log():
     transform_log = transform_log_from_screw_matrix(S_mat, theta)
     S_mat2, theta2 = screw_matrix_from_transform_log(transform_log)
     assert_array_almost_equal(S_mat, S_mat2)
-    assert_almost_equal(theta, theta2)
+    assert pytest.approx(theta) == theta2
 
     S_mat = np.zeros((4, 4))
     theta = 0.0
     transform_log = transform_log_from_screw_matrix(S_mat, theta)
     S_mat2, theta2 = screw_matrix_from_transform_log(transform_log)
     assert_array_almost_equal(S_mat, S_mat2)
-    assert_almost_equal(theta, theta2)
+    assert pytest.approx(theta) == theta2
 
     random_state = np.random.RandomState(65)
     for _ in range(5):
@@ -579,7 +569,7 @@ def test_conversions_between_screw_matrix_and_transform_log():
         transform_log = transform_log_from_screw_matrix(S_mat, theta)
         S_mat2, theta2 = screw_matrix_from_transform_log(transform_log)
         assert_array_almost_equal(S_mat, S_mat2)
-        assert_almost_equal(theta, theta2)
+        assert pytest.approx(theta) == theta2
 
 
 def test_conversions_between_transform_and_transform_log():
@@ -620,23 +610,21 @@ def test_adjoint_of_transformation():
 
         S_B, theta_dot2 = screw_axis_from_exponential_coordinates(V_B)
         V_mat_B2 = screw_matrix_from_screw_axis(S_B) * theta_dot2
-        assert_almost_equal(theta_dot, theta_dot2)
+        assert pytest.approx(theta_dot) == theta_dot2
         assert_array_almost_equal(V_mat_B, V_mat_B2)
 
 
 def test_check_dual_quaternion():
     dq1 = [0, 0]
-    assert_raises_regexp(
-        ValueError, "Expected dual quaternion",
-        check_dual_quaternion, dq1)
+    with pytest.raises(ValueError, match="Expected dual quaternion"):
+        check_dual_quaternion(dq1)
 
     dq2 = [[0, 0]]
-    assert_raises_regexp(
-        ValueError, "Expected dual quaternion",
-        check_dual_quaternion, dq2)
+    with pytest.raises(ValueError, match="Expected dual quaternion"):
+        check_dual_quaternion(dq2)
 
     dq3 = check_dual_quaternion([0] * 8, unit=False)
-    assert_equal(dq3.shape[0], 8)
+    assert dq3.shape[0] == 8
 
 
 def test_normalize_dual_quaternion():
@@ -723,11 +711,9 @@ def test_assert_screw_parameters_equal():
         q, s_axis, h, theta,
         q + 484.3 * s_axis, s_axis, h, theta)
 
-    assert_raises_regexp(
-        AssertionError, "-1492.7128508189376 != 995.1419005459584",
-        assert_screw_parameters_equal,
-        q, s_axis, h, theta,
-        q + 484.3, s_axis, h, theta)
+    with pytest.raises(AssertionError):
+        assert_screw_parameters_equal(
+            q, s_axis, h, theta, q + 484.3, s_axis, h, theta)
 
     s_axis_mirrored = -s_axis
     theta_mirrored = 2.0 * np.pi - theta
@@ -742,15 +728,15 @@ def test_screw_parameters_from_dual_quaternion():
     q, s_axis, h, theta = screw_parameters_from_dual_quaternion(dq)
     assert_array_almost_equal(q, np.zeros(3))
     assert_array_almost_equal(s_axis, np.array([1, 0, 0]))
-    assert_true(np.isinf(h))
-    assert_almost_equal(theta, 0)
+    assert np.isinf(h)
+    assert pytest.approx(theta) == 0
 
     dq = dual_quaternion_from_pq(np.array([1.2, 1.3, 1.4, 1, 0, 0, 0]))
     q, s_axis, h, theta = screw_parameters_from_dual_quaternion(dq)
     assert_array_almost_equal(q, np.zeros(3))
     assert_array_almost_equal(s_axis, norm_vector(np.array([1.2, 1.3, 1.4])))
-    assert_true(np.isinf(h))
-    assert_almost_equal(theta, np.linalg.norm(np.array([1.2, 1.3, 1.4])))
+    assert np.isinf(h)
+    assert pytest.approx(theta) == np.linalg.norm(np.array([1.2, 1.3, 1.4]))
 
     random_state = np.random.RandomState(1001)
     quat = random_quaternion(random_state)
@@ -874,7 +860,7 @@ def test_dual_quaternion_sclerp_sign_ambiguity():
         [np.linalg.norm(r - s)
          for r, s in zip(traj_q_opposing[:-1], traj_q_opposing[1:])])
 
-    assert_equal(path_length_opposing, path_length)
+    assert path_length_opposing == path_length
 
 
 def test_exponential_coordinates_from_almost_identity_transform():
@@ -918,9 +904,8 @@ def test_exponential_coordinates_from_transform_without_check():
 
 def test_transform_from_exponential_coordinates_without_check():
     Stheta = np.zeros(7)
-    assert_raises_regexp(
-        ValueError, "could not broadcast input array",
-        transform_from_exponential_coordinates, Stheta, check=False)
+    with pytest.raises(ValueError, match="could not broadcast input array"):
+        transform_from_exponential_coordinates(Stheta, check=False)
 
 
 def test_adjoint_from_transform_without_check():
diff --git a/pytransform3d/test/test_urdf.py b/pytransform3d/test/test_urdf.py
index cc111ce51..d91465770 100644
--- a/pytransform3d/test/test_urdf.py
+++ b/pytransform3d/test/test_urdf.py
@@ -12,8 +12,7 @@
     initialize_urdf_transform_manager)
 from pytransform3d.transformations import transform_from
 from numpy.testing import assert_array_almost_equal
-from nose.tools import assert_raises, assert_equal, assert_true, assert_in
-from nose import SkipTest
+import pytest
 
 
 COMPI_URDF = """
@@ -93,16 +92,19 @@
 
 
 def test_missing_robot_tag():
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, "")
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf("")
 
 
 def test_missing_robot_name():
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, "<robot/>")
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf("<robot/>")
 
 
 def test_missing_link_name():
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf,
-                  "<robot name=\"robot_name\"><link/></robot>")
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(
+            "<robot name=\"robot_name\"><link/></robot>")
 
 
 def test_missing_joint_name():
@@ -116,7 +118,8 @@ def test_missing_joint_name():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_missing_parent():
@@ -129,7 +132,8 @@ def test_missing_parent():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_missing_child():
@@ -142,7 +146,8 @@ def test_missing_child():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_missing_parent_link_name():
@@ -156,7 +161,8 @@ def test_missing_parent_link_name():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_missing_child_link_name():
@@ -170,7 +176,8 @@ def test_missing_child_link_name():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_reference_to_unknown_child():
@@ -183,7 +190,8 @@ def test_reference_to_unknown_child():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_reference_to_unknown_parent():
@@ -196,7 +204,8 @@ def test_reference_to_unknown_parent():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_missing_joint_type():
@@ -210,7 +219,8 @@ def test_missing_joint_type():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_without_origin():
@@ -259,7 +269,8 @@ def test_unsupported_joint_type():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_unknown_joint_type():
@@ -273,7 +284,8 @@ def test_unknown_joint_type():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_with_empty_axis():
@@ -334,7 +346,8 @@ def test_joint_limits():
     tm = UrdfTransformManager()
     tm.load_urdf(COMPI_URDF)
 
-    assert_raises(KeyError, tm.get_joint_limits, "unknown_joint")
+    with pytest.raises(KeyError):
+        tm.get_joint_limits("unknown_joint")
     assert_array_almost_equal(tm.get_joint_limits("joint1"), (-1, 1))
     assert_array_almost_equal(tm.get_joint_limits("joint2"), (-1, np.inf))
     assert_array_almost_equal(tm.get_joint_limits("joint3"), (-np.inf, 1))
@@ -384,7 +397,7 @@ def test_fixed_joint_unchanged_and_warning():
     tcp_to_link0_before = tm.get_transform("tcp", "linkmount")
     with warnings.catch_warnings(record=True) as w:
         tm.set_joint("jointtcp", 2.0)
-    assert_equal(len(w), 1)
+    assert len(w) == 1
     tcp_to_link0_after = tm.get_transform("tcp", "linkmount")
     assert_array_almost_equal(
         tcp_to_link0_before,
@@ -395,7 +408,8 @@ def test_fixed_joint_unchanged_and_warning():
 def test_unknown_joint():
     tm = UrdfTransformManager()
     tm.load_urdf(COMPI_URDF)
-    assert_raises(KeyError, tm.set_joint, "unknown_joint", 0)
+    with pytest.raises(KeyError):
+        tm.set_joint("unknown_joint", 0)
 
 
 def test_visual_without_geometry():
@@ -414,7 +428,8 @@ def test_visual_without_geometry():
     </joint>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_visual():
@@ -498,12 +513,12 @@ def test_unique_frame_names():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_in("robot_name", tm.nodes)
-    assert_in("link0", tm.nodes)
-    assert_in("link1", tm.nodes)
-    assert_in("visual:link1/link1_geometry", tm.nodes)
-    assert_in("collision:link1/link1_geometry", tm.nodes)
-    assert_equal(len(tm.nodes), 5)
+    assert "robot_name" in tm.nodes
+    assert "link0" in tm.nodes
+    assert "link1" in tm.nodes
+    assert "visual:link1/link1_geometry" in tm.nodes
+    assert "collision:link1/link1_geometry" in tm.nodes
+    assert len(tm.nodes) == 5
 
 
 def test_collision_box():
@@ -522,7 +537,7 @@ def test_collision_box():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.collision_objects), 1)
+    assert len(tm.collision_objects) == 1
     assert_array_almost_equal(tm.collision_objects[0].size,
                               np.array([2, 3, 4]))
 
@@ -543,7 +558,7 @@ def test_collision_box_without_size():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.collision_objects), 1)
+    assert len(tm.collision_objects) == 1
     assert_array_almost_equal(tm.collision_objects[0].size, np.zeros(3))
 
 
@@ -563,8 +578,8 @@ def test_collision_sphere():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.collision_objects), 1)
-    assert_equal(tm.collision_objects[0].radius, 0.123)
+    assert len(tm.collision_objects) == 1
+    assert tm.collision_objects[0].radius == 0.123
 
 
 def test_collision_sphere_without_radius():
@@ -580,7 +595,8 @@ def test_collision_sphere_without_radius():
     </link>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_collision_cylinder():
@@ -599,9 +615,9 @@ def test_collision_cylinder():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.collision_objects), 1)
-    assert_equal(tm.collision_objects[0].radius, 0.123)
-    assert_equal(tm.collision_objects[0].length, 1.234)
+    assert len(tm.collision_objects) == 1
+    assert tm.collision_objects[0].radius == 0.123
+    assert tm.collision_objects[0].length == 1.234
 
 
 def test_collision_cylinder_without_radius():
@@ -617,7 +633,8 @@ def test_collision_cylinder_without_radius():
     </link>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_collision_cylinder_without_length():
@@ -633,7 +650,8 @@ def test_collision_cylinder_without_length():
     </link>
     </robot>
     """
-    assert_raises(UrdfException, UrdfTransformManager().load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_multiple_collision_objects():
@@ -660,7 +678,7 @@ def test_multiple_collision_objects():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.collision_objects), 2)
+    assert len(tm.collision_objects) == 2
 
 
 def test_multiple_visuals():
@@ -687,7 +705,7 @@ def test_multiple_visuals():
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
 
-    assert_equal(len(tm.visuals), 2)
+    assert len(tm.visuals) == 2
 
 
 def test_multiple_parents():
@@ -717,7 +735,7 @@ def test_multiple_parents():
 
     p0c = tm.get_transform("parent0", "child")
     p1c = tm.get_transform("parent1", "child")
-    assert_equal(p0c[0, 3], p1c[1, 3])
+    assert p0c[0, 3] == p1c[1, 3]
 
 
 def test_mesh_missing_filename():
@@ -752,13 +770,13 @@ def test_mesh_missing_filename():
 
     </robot>
     """
-    tm = UrdfTransformManager()
-    assert_raises(UrdfException, tm.load_urdf, urdf, mesh_path="")
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf, mesh_path="")
 
 
 def test_plot_mesh_smoke_without_scale():
     if not matplotlib_available:
-        raise SkipTest("matplotlib is required for this test")
+        pytest.skip("matplotlib is required for this test")
 
     urdf = """
     <?xml version="1.0"?>
@@ -862,7 +880,7 @@ def test_prismatic_joint():
 
 def test_plot_mesh_smoke_with_scale():
     if not matplotlib_available:
-        raise SkipTest("matplotlib is required for this test")
+        pytest.skip("matplotlib is required for this test")
 
     BASE_DIR = "test/test_data/"
     tm = UrdfTransformManager()
@@ -878,7 +896,7 @@ def test_plot_mesh_smoke_with_scale():
 
 def test_plot_without_mesh():
     if not matplotlib_available:
-        raise SkipTest("matplotlib is required for this test")
+        pytest.skip("matplotlib is required for this test")
 
     BASE_DIR = "test/test_data/"
     tm = UrdfTransformManager()
@@ -892,7 +910,7 @@ def test_plot_without_mesh():
 
     with warnings.catch_warnings(record=True) as w:
         tm.plot_visuals("lower_cone", ax=ax)
-        assert_equal(len(w), 1)
+        assert len(w) == 1
 
 
 def test_parse_material():
@@ -933,7 +951,8 @@ def test_parse_material_without_name():
     </robot>
     """
     tm = UrdfTransformManager()
-    assert_raises(UrdfException, tm.load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_parse_material_without_color():
@@ -953,7 +972,7 @@ def test_parse_material_without_color():
     """
     tm = UrdfTransformManager()
     tm.load_urdf(urdf)
-    assert_true(tm.visuals[0].color is None)
+    assert tm.visuals[0].color is None
 
 
 def test_parse_material_without_rgba():
@@ -974,7 +993,8 @@ def test_parse_material_without_rgba():
     </robot>
     """
     tm = UrdfTransformManager()
-    assert_raises(UrdfException, tm.load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_parse_material_with_two_colors():
@@ -996,7 +1016,8 @@ def test_parse_material_with_two_colors():
     </robot>
     """
     tm = UrdfTransformManager()
-    assert_raises(UrdfException, tm.load_urdf, urdf)
+    with pytest.raises(UrdfException):
+        UrdfTransformManager().load_urdf(urdf)
 
 
 def test_parse_material_local():
@@ -1022,7 +1043,7 @@ def test_parse_material_local():
 
 def test_plot_mesh_smoke_with_package_dir():
     if not matplotlib_available:
-        raise SkipTest("matplotlib is required for this test")
+        pytest.skip("matplotlib is required for this test")
 
     urdf = """
     <?xml version="1.0"?>
@@ -1056,10 +1077,10 @@ def test_load_inertial_info():
     </robot>
     """
     _, links, _ = parse_urdf(urdf)
-    assert_equal(len(links), 1)
-    assert_equal(links[0].name, "cone")
+    assert len(links) == 1
+    assert links[0].name == "cone"
     assert_array_almost_equal(links[0].inertial_frame, np.eye(4))
-    assert_equal(links[0].mass, 0.001)
+    assert links[0].mass == 0.001
     assert_array_almost_equal(links[0].inertia, np.zeros((3, 3)))
 
 
@@ -1077,10 +1098,10 @@ def test_load_inertial_info_sparse_matrix():
     </robot>
     """
     _, links, _ = parse_urdf(urdf)
-    assert_equal(len(links), 1)
-    assert_equal(links[0].name, "cone")
+    assert len(links) == 1
+    assert links[0].name == "cone"
     assert_array_almost_equal(links[0].inertial_frame, np.eye(4))
-    assert_equal(links[0].mass, 0.001)
+    assert links[0].mass == 0.001
     assert_array_almost_equal(links[0].inertia, np.zeros((3, 3)))
 
 
@@ -1098,10 +1119,10 @@ def test_load_inertial_info_diagonal_matrix():
     </robot>
     """
     _, links, _ = parse_urdf(urdf)
-    assert_equal(len(links), 1)
-    assert_equal(links[0].name, "cone")
+    assert len(links) == 1
+    assert links[0].name == "cone"
     assert_array_almost_equal(links[0].inertial_frame, np.eye(4))
-    assert_equal(links[0].mass, 0.001)
+    assert links[0].mass == 0.001
     assert_array_almost_equal(links[0].inertia, np.eye(3))
 
 
@@ -1118,10 +1139,10 @@ def test_load_inertial_info_without_matrix():
     </robot>
     """
     _, links, _ = parse_urdf(urdf)
-    assert_equal(len(links), 1)
-    assert_equal(links[0].name, "cone")
+    assert len(links) == 1
+    assert links[0].name == "cone"
     assert_array_almost_equal(links[0].inertial_frame, np.eye(4))
-    assert_equal(links[0].mass, 0.001)
+    assert links[0].mass == 0.001
     assert_array_almost_equal(links[0].inertia, np.zeros((3, 3)))
 
 
@@ -1138,18 +1159,18 @@ def test_load_inertial_info_without_mass():
     </robot>
     """
     _, links, _ = parse_urdf(urdf)
-    assert_equal(len(links), 1)
-    assert_equal(links[0].name, "cone")
+    assert len(links) == 1
+    assert links[0].name == "cone"
     assert_array_almost_equal(links[0].inertial_frame, np.eye(4))
-    assert_equal(links[0].mass, 0.0)
+    assert links[0].mass == 0.0
     assert_array_almost_equal(links[0].inertia, np.zeros((3, 3)))
 
 
 def test_load_urdf_functional():
     robot_name, links, joints = parse_urdf(COMPI_URDF)
-    assert_equal(robot_name, "compi")
-    assert_equal(len(links), 8)
-    assert_equal(len(joints), 7)
+    assert robot_name == "compi"
+    assert len(links) == 8
+    assert len(joints) == 7
 
     tm = UrdfTransformManager()
     initialize_urdf_transform_manager(tm, robot_name, links, joints)
diff --git a/pytransform3d/transformations/_testing.py b/pytransform3d/transformations/_testing.py
index 8225f3115..01e142c9f 100644
--- a/pytransform3d/transformations/_testing.py
+++ b/pytransform3d/transformations/_testing.py
@@ -1,7 +1,7 @@
 """Testing utilities."""
 import numpy as np
 from numpy.testing import assert_array_almost_equal
-from ..rotations import assert_rotation_matrix, norm_angle
+from ..rotations import assert_rotation_matrix, norm_angle, eps
 from ._dual_quaternion_operations import (
     dq_q_conj, concatenate_dual_quaternions)
 
@@ -106,8 +106,6 @@ def assert_screw_parameters_equal(
     Note that the screw axis can be inverted. In this case theta and h have
     to be adapted.
 
-    This function needs the dependency nose.
-
     Parameters
     ----------
     q1 : array, shape (3,)
@@ -146,8 +144,6 @@ def assert_screw_parameters_equal(
         Positional arguments that will be passed to
         `assert_array_almost_equal`
     """
-    from nose.tools import assert_almost_equal
-
     # normalize thetas
     theta1_new = norm_angle(theta1)
     h1 *= theta1 / theta1_new
@@ -162,12 +158,12 @@ def assert_screw_parameters_equal(
     # matter since they should be identical or mirrored)
     q1_to_q2 = q2 - q1
     factors = q1_to_q2 / s_axis2
-    assert_almost_equal(factors[0], factors[1])
-    assert_almost_equal(factors[1], factors[2])
+    assert abs(factors[0] - factors[1]) < eps
+    assert abs(factors[1] - factors[2]) < eps
     try:
         assert_array_almost_equal(s_axis1, s_axis2, *args, **kwargs)
-        assert_almost_equal(h1, h2)
-        assert_almost_equal(theta1, theta2)
+        assert abs(h1 - h2) < eps
+        assert abs(theta1 - theta2) < eps
     except AssertionError:  # possibly mirrored screw axis
         s_axis1_new = -s_axis1
         # make sure that we keep the direction of rotation
@@ -182,5 +178,5 @@ def assert_screw_parameters_equal(
         theta1 = theta1_new
 
         assert_array_almost_equal(s_axis1_new, s_axis2, *args, **kwargs)
-        assert_almost_equal(h1, h2)
-        assert_almost_equal(theta1, theta2)
+        assert abs(h1 - h2) < eps
+        assert abs(theta1 - theta2) < eps