diff --git a/tests/postprocessor/test_autoreg.py b/tests/postprocessor/test_autoreg.py
new file mode 100644
index 0000000000000000000000000000000000000000..df07324f4a044aa2bbead45f31493efaff716ee2
--- /dev/null
+++ b/tests/postprocessor/test_autoreg.py
@@ -0,0 +1,41 @@
+import numpy as np
+import pandas as pd
+import pytest
+
+from postprocessor.core.processes.autoreg import autoreg, autoregParameters
+
+
+def generate_sinusoids_df(
+    time_axis,
+    list_freqs,
+):
+    """Generate sinusoids and put them in a dataframe.
+
+    Parameters ---------- time_axis : array_like     Time axis.
+    list_freqs : list     List of frequencies for the sinusoids
+    Examples -------- generate_sinusoids_df([0,1,2,3,4], [1,2,3])
+    produces a dataframe containing 3 rows.  The first row has a
+    sinusoid of frequency 1, the second has frequency 2, and the third
+    has frequency 3. The time axis goes from 0 to 5.
+    """
+    sinusoids = np.array(
+        [np.sin((2 * np.pi * freq) * time_axis) for freq in list_freqs]
+    )
+    return pd.DataFrame(sinusoids)
+
+
+@pytest.mark.parametrize("time_axis", [np.arange(0, 10, 0.01)])
+@pytest.mark.parametrize("list_freqs", [[1, 2, 3]])
+def test_autoreg(
+    time_axis,
+    list_freqs,
+):
+    """Tests autoreg.
+
+    Tests whether an autoreg runner can be initialised with default
+    parameters and runs without errors.
+    """
+    dummy_signal = generate_sinusoids_df(time_axis, list_freqs)
+    autoreg_runner = autoreg(autoregParameters.default())
+    # freqs_df, power_df, order_df = autoreg_runner.run(dummy_signal)
+    _, _, _ = autoreg_runner.run(dummy_signal)
diff --git a/tests/postprocessor/test_gpsignal.py b/tests/postprocessor/test_gpsignal.py
new file mode 100644
index 0000000000000000000000000000000000000000..6bc4e9f2b7d3e4ffab660e20606c1336fdd38c06
--- /dev/null
+++ b/tests/postprocessor/test_gpsignal.py
@@ -0,0 +1,53 @@
+import numpy as np
+import pandas as pd
+import pytest
+
+from postprocessor.core.processes.gpsignal import (
+    estimate_gr,
+    gpsignal,
+    gpsignalParameters,
+)
+
+
+def dummy_signal(n_cells, n_tps, noise_level):
+    signal = np.array([np.linspace(1, 2, n_tps) for _ in range(n_cells)])
+    noise = np.random.normal(scale=noise_level, size=signal.shape)
+    return pd.DataFrame(signal + noise)
+
+
+def test_dummy_signal():
+    ds = dummy_signal(5, 10, 0.001)
+    assert len(ds.columns) == 10
+    assert len(ds) == 5
+    # assert np.isclose(ds.std(), 0.001).any()
+
+
+def default_values():
+    return dict(
+        dt=1, noruns=5, bounds={0: (0, 2), 1: (1, 3), 2: (-8, 0)}, verbose=True
+    )
+
+
+# TODO: the tolerance threshold still needs to be tuned to expectations
+thresh = 0.1
+np.random.seed(42)
+
+
+@pytest.mark.parametrize("n_cells", [10])
+@pytest.mark.parametrize("n_tps", [50])
+@pytest.mark.parametrize("noise_level", [0.01])
+def test_estimate_gr(n_cells, n_tps, noise_level):
+    ds = dummy_signal(n_cells, n_tps, noise_level)
+    # Growth rate is just the slope
+    gr = 1 / n_tps
+    for i, volume in ds.iterrows():
+        results = estimate_gr(volume, **default_values())
+        est_gr = results["growth_rate"]
+        assert np.allclose(est_gr, gr, rtol=thresh), f"Failed for cell {i}"
+
+
+def test_gpsignal():
+    ds = dummy_signal(5, 10, 0.001)
+    gpsig = gpsignal(gpsignalParameters.default())
+    multi_signal = gpsig.run(ds)
+    assert "fit_volume" in multi_signal
diff --git a/tests/postprocessor/test_mi.py b/tests/postprocessor/test_mi.py
new file mode 100644
index 0000000000000000000000000000000000000000..50265695de86f70427bca80cf56fabbe8689bd1d
--- /dev/null
+++ b/tests/postprocessor/test_mi.py
@@ -0,0 +1,159 @@
+"""
+Mutual information test
+"""
+# import pytest
+import numpy as np
+import pandas as pd
+
+from postprocessor.core.multisignal.mi import mi, miParameters
+
+# Sample data: cropped from replicate 1 in
+# https://github.com/swainlab/mi-by-decoding/blob/master/matlab/ExampleScript/fig1_sfp1_replicates.json
+SIGNAL_RICH = np.array(
+    [
+        [
+            -1.39512436686014,
+            -1.44046750531481,
+            -1.67185664648421,
+            -0.500474684796053,
+            -0.97255340345062,
+            -1.16250137971723,
+        ],
+        [
+            -0.407742899002175,
+            -0.619583901332133,
+            -0.466156867298538,
+            -0.69093319800047,
+            -0.186360950573155,
+            -0.791411909242518,
+        ],
+        [
+            0.350152741857363,
+            0.913407870351929,
+            0.524645770050427,
+            0.441917565610652,
+            1.5228639153911,
+            2.67310873357743,
+        ],
+        [
+            0.524953681848925,
+            0.653076029386848,
+            1.24582647626295,
+            0.776211754098582,
+            -0.355200015764816,
+            -0.0871128171616209,
+        ],
+        [
+            -1.68461323842732,
+            -1.43594025257403,
+            -1.3114696359734,
+            -0.956125193215477,
+            -1.2863334639258,
+            -0.963653392884438,
+        ],
+        [
+            0.657671105178289,
+            1.20192526734078,
+            1.41272977531711,
+            1.10313719899755,
+            1.21218191767352,
+            1.25148540716015,
+        ],
+    ]
+)
+
+SIGNAL_STRESS = np.array(
+    [
+        [
+            0.360683309928096,
+            0.653056477804747,
+            0.609421809463519,
+            0.26028011016996,
+            -0.163807667201703,
+            -0.725067314828773,
+        ],
+        [
+            -1.7884489956977,
+            -1.77274508168164,
+            -1.38542947325363,
+            -1.11368924913116,
+            -1.54227678929895,
+            -1.67197618502403,
+        ],
+        [
+            0.246852644985541,
+            0.961545692641162,
+            -0.159373062144918,
+            0.0990542384881887,
+            -0.766446517169187,
+            -1.20071098737371,
+        ],
+        [
+            0.393236272245847,
+            0.441250356135278,
+            1.05344010654052,
+            1.06399045807211,
+            -0.305342136100235,
+            -1.49833740305382,
+        ],
+        [
+            -1.45454923818794,
+            -1.07292739455483,
+            -1.2991307659611,
+            -1.15322537661844,
+            -1.29894837314545,
+            -1.8884055407594,
+        ],
+        [
+            0.102130222571265,
+            -1.07499178276524,
+            -1.3148530608215,
+            -0.765688535324232,
+            -0.645377669611553,
+            -0.937035540900562,
+        ],
+    ]
+)
+
+#  Expected output: produced by running a modified estimateMI() from
+#  https://git.ecdf.ed.ac.uk/pswain/mutual-information/-/blob/master/MIdecoding.py
+#  on the sample input data.  The modification was adding the few lines that
+# forced a random state for each bootstrap as the 'i' variable, so that
+# the output isconsistent and therefore useful for pytest.
+MI_IQR = np.array(
+    [
+        [0, 0, 0],
+        [0, 0, 0],
+        [0, 0, 0],
+        [0, 0, 0],
+        [0.311278124459133, 0, 0.311278124459133],
+        [0.311278124459133, 0.311278124459133, 0.311278124459133],
+    ]
+)
+
+
+def convert_to_df(array, strain_name):
+    multiindex_array = [[strain_name] * len(array), list(range(len(array)))]
+    multiindex = pd.MultiIndex.from_arrays(
+        multiindex_array, names=("strain", "cellID")
+    )
+    signal = pd.DataFrame(array, multiindex)
+
+    return signal
+
+
+def test_mi():
+    """Tests mi.
+
+    Tests whether an mi runner can be initialised with default
+    parameters and runs on sample data, giving expected output.
+    """
+    dummy_signals = [
+        convert_to_df(SIGNAL_RICH, "rich"),
+        convert_to_df(SIGNAL_STRESS, "stress"),
+    ]
+    params = miParameters.default()
+    params.train_test_split_seeding = True
+    mi_runner = mi(params)
+    res = mi_runner.run(dummy_signals)
+    assert np.allclose(res, MI_IQR)