diff --git a/model_server/base/roiset.py b/model_server/base/roiset.py
index 8b7642774a4e84c9f476b5aa744ec62652ed551e..cfeec9eec6d4de1e2d56a358055140a58893b4a6 100644
--- a/model_server/base/roiset.py
+++ b/model_server/base/roiset.py
@@ -954,7 +954,7 @@ class RoiSet(object):
 
         return interm
 
-    def serialize(self, where: Path, prefix='') -> dict:
+    def serialize(self, where: Path, prefix='roiset') -> dict:
         """
         Export the minimal information needed to recreate RoiSet object, i.e. CSV data file and tight patch masks
         :param where: path of directory in which to write files
diff --git a/model_server/conf/testing.py b/model_server/conf/testing.py
index 12d93c8b884dfad672a5e7adf0f5cbe05c60eeba..042a95512bb4417efd0e09f1359db11a37e4ff45 100644
--- a/model_server/conf/testing.py
+++ b/model_server/conf/testing.py
@@ -1,13 +1,18 @@
 import json
 import os
 import unittest
+from math import floor
 from multiprocessing import Process
 from pathlib import Path
 from shutil import copyfile
 
+import numpy as np
 import requests
 from urllib3 import Retry
 
+from ..base.accessors import GenericImageDataAccessor, InMemoryDataAccessor
+from ..base.models import SemanticSegmentationModel, InstanceSegmentationModel
+
 from ..base.accessors import generate_file_accessor
 
 class TestServerBaseClass(unittest.TestCase):
@@ -77,9 +82,21 @@ def setup_test_data():
     :return:
         meta (dict) of test data and paths
     """
+
+    def _winpath(f):
+        if not isinstance(f, str):
+            return f
+        p = f.split('/')
+        if len(p) > 1:
+            p[1] = p[1] + ':'
+            return '\\'.join(p[1:])
+        else:
+            return f
+
     # places to look for test data
     data_paths = [
         os.environ.get('UNITTEST_DATA_ROOT'),
+        _winpath(os.environ.get('UNITTEST_DATA_ROOT')),
         Path.home() / 'model_server' / 'testing',
         os.getcwd(),
     ]
@@ -100,8 +117,7 @@ def setup_test_data():
         raise Exception('Could not find test data, try setting environmental variable UNITTEST_DATA_ROOT.')
 
     meta['root'] = Path(root)
-    op_ev = os.environ.get('UNITTEST_OUTPUT', (meta['root'] / 'test_output'))
-    meta['output_path'] = Path(op_ev)
+    meta['output_path'] = meta['root'] / 'test_output'
     meta['output_path'].mkdir(parents=True, exist_ok=True)
 
     # resolve relative paths
@@ -117,4 +133,49 @@ def setup_test_data():
     return meta
 
 # object containing test data paths and metadata, for import into unittest modules
-meta = setup_test_data()
\ No newline at end of file
+meta = setup_test_data()
+
+
+class DummySemanticSegmentationModel(SemanticSegmentationModel):
+
+    model_id = 'dummy_make_white_square'
+
+    def load(self):
+        return True
+
+    def infer(self, img: GenericImageDataAccessor) -> (GenericImageDataAccessor, dict):
+        super().infer(img)
+        w = img.shape_dict['X']
+        h = img.shape_dict['Y']
+        result = np.zeros([h, w], dtype='uint8')
+        result[floor(0.25 * h) : floor(0.75 * h), floor(0.25 * w) : floor(0.75 * w)] = 255
+        return InMemoryDataAccessor(data=result), {'success': True}
+
+    def label_pixel_class(
+            self, img: GenericImageDataAccessor, **kwargs) -> GenericImageDataAccessor:
+        mask, _ = self.infer(img)
+        return mask
+
+
+class DummyInstanceSegmentationModel(InstanceSegmentationModel):
+
+    model_id = 'dummy_pass_input_mask'
+
+    def load(self):
+        return True
+
+    def infer(
+            self, img: GenericImageDataAccessor, mask: GenericImageDataAccessor
+    ) -> (GenericImageDataAccessor, dict):
+        return img.__class__(
+            (mask.data / mask.data.max()).astype('uint16')
+        )
+
+    def label_instance_class(
+            self, img: GenericImageDataAccessor, mask: GenericImageDataAccessor, **kwargs
+    ) -> GenericImageDataAccessor:
+        """
+        Returns a trivial segmentation, i.e. the input mask with value 1
+        """
+        super(DummyInstanceSegmentationModel, self).label_instance_class(img, mask, **kwargs)
+        return self.infer(img, mask)
diff --git a/pyproject.toml b/pyproject.toml
index d00d683d3e31b3bf58975c2cd9342c4a0c8ab298..882ad79c5a6e8bf5d99116212482db65478df8a3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
 [project]
 name = "model_server"
 license = {file = "LICENSE"}
-version = "2024.09.30"
+version = "2024.10.01"
 authors = [
   { name="Christopher Rhodes", email="christopher.rhodes@embl.de" },
 ]
diff --git a/tests/base/test_api.py b/tests/base/test_api.py
index f368dc05212c59f1b2ece208a719b4e249cedf70..e31b4366f3d4baa9371fcd80c3338cc2c5fbb013 100644
--- a/tests/base/test_api.py
+++ b/tests/base/test_api.py
@@ -8,7 +8,7 @@ import model_server.conf.testing as conf
 from model_server.base.accessors import InMemoryDataAccessor
 from model_server.base.api import app
 from model_server.base.session import session
-from tests.base.test_model import DummyInstanceSegmentationModel, DummySemanticSegmentationModel
+from model_server.conf.testing import DummySemanticSegmentationModel, DummyInstanceSegmentationModel
 
 czifile = conf.meta['image_files']['czifile']
 
diff --git a/tests/base/test_model.py b/tests/base/test_model.py
index fb5a8f21e263ea5ab5f08d31f727ddcd24386e63..d975f7cd8725e0215391b4a526feab7cd69eeb31 100644
--- a/tests/base/test_model.py
+++ b/tests/base/test_model.py
@@ -1,58 +1,12 @@
-from math import floor
 import unittest
 
-import numpy as np
-
 import model_server.conf.testing as conf
-from model_server.base.accessors import CziImageFileAccessor, GenericImageDataAccessor, InMemoryDataAccessor
-from model_server.base.models import CouldNotLoadModelError, InstanceSegmentationModel, SemanticSegmentationModel, BinaryThresholdSegmentationModel
+from model_server.conf.testing import DummySemanticSegmentationModel, DummyInstanceSegmentationModel
+from model_server.base.accessors import CziImageFileAccessor
+from model_server.base.models import CouldNotLoadModelError, BinaryThresholdSegmentationModel
 
 czifile = conf.meta['image_files']['czifile']
 
-class DummySemanticSegmentationModel(SemanticSegmentationModel):
-
-    model_id = 'dummy_make_white_square'
-
-    def load(self):
-        return True
-
-    def infer(self, img: GenericImageDataAccessor) -> (GenericImageDataAccessor, dict):
-        super().infer(img)
-        w = img.shape_dict['X']
-        h = img.shape_dict['Y']
-        result = np.zeros([h, w], dtype='uint8')
-        result[floor(0.25 * h) : floor(0.75 * h), floor(0.25 * w) : floor(0.75 * w)] = 255
-        return InMemoryDataAccessor(data=result), {'success': True}
-
-    def label_pixel_class(
-            self, img: GenericImageDataAccessor, **kwargs) -> GenericImageDataAccessor:
-        mask, _ = self.infer(img)
-        return mask
-
-
-class DummyInstanceSegmentationModel(InstanceSegmentationModel):
-
-    model_id = 'dummy_pass_input_mask'
-
-    def load(self):
-        return True
-
-    def infer(
-            self, img: GenericImageDataAccessor, mask: GenericImageDataAccessor
-    ) -> (GenericImageDataAccessor, dict):
-        return img.__class__(
-            (mask.data / mask.data.max()).astype('uint16')
-        )
-
-    def label_instance_class(
-            self, img: GenericImageDataAccessor, mask: GenericImageDataAccessor, **kwargs
-    ) -> GenericImageDataAccessor:
-        """
-        Returns a trivial segmentation, i.e. the input mask with value 1
-        """
-        super(DummyInstanceSegmentationModel, self).label_instance_class(img, mask, **kwargs)
-        return self.infer(img, mask)
-
 
 class TestCziImageFileAccess(unittest.TestCase):
     def setUp(self) -> None:
diff --git a/tests/base/test_pipelines.py b/tests/base/test_pipelines.py
index 2a97c5b4088976f96a4973e0f70e8472fc722047..9f1b0303cbacf9fe9fe9969f7beadfe1ce942711 100644
--- a/tests/base/test_pipelines.py
+++ b/tests/base/test_pipelines.py
@@ -4,7 +4,7 @@ from model_server.base.accessors import generate_file_accessor, write_accessor_d
 from model_server.base.pipelines import router, segment, segment_zproj
 
 import model_server.conf.testing as conf
-from tests.base.test_model import DummySemanticSegmentationModel
+from model_server.conf.testing import DummySemanticSegmentationModel
 
 czifile = conf.meta['image_files']['czifile']
 zstack = conf.meta['image_files']['tifffile']
diff --git a/tests/base/test_roiset.py b/tests/base/test_roiset.py
index 0a03973e78ca824083f2821157c70bb9adc77265..785358c92961662273f97f8f8c74037e03fc3cf3 100644
--- a/tests/base/test_roiset.py
+++ b/tests/base/test_roiset.py
@@ -11,7 +11,7 @@ from model_server.base.roiset import filter_df_overlap_bbox, filter_df_overlap_s
 from model_server.base.roiset import RoiSet
 from model_server.base.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file, PatchStack
 import model_server.conf.testing as conf
-from tests.base.test_model import DummyInstanceSegmentationModel
+from model_server.conf.testing import DummyInstanceSegmentationModel
 
 data = conf.meta['image_files']
 output_path = conf.meta['output_path']
diff --git a/tests/base/test_roiset_derived.py b/tests/base/test_roiset_derived.py
index 52e7f6fc0a917d719262dcd0c19f3170414f2f18..156ef9fe42c472dc829085ed3b8c26bb46ac003a 100644
--- a/tests/base/test_roiset_derived.py
+++ b/tests/base/test_roiset_derived.py
@@ -7,7 +7,7 @@ from model_server.base.roiset import RoiSetWithDerivedChannelsExportParams, RoiS
 from model_server.base.roiset import RoiSetWithDerivedChannels
 from model_server.base.accessors import generate_file_accessor, PatchStack
 import model_server.conf.testing as conf
-from tests.base.test_model import DummyInstanceSegmentationModel
+from model_server.conf.testing import DummyInstanceSegmentationModel
 
 data = conf.meta['image_files']
 params = conf.meta['roiset']