diff --git a/scrapy_autounit/middleware.py b/scrapy_autounit/middleware.py
index 0568541..8bc61a7 100644
--- a/scrapy_autounit/middleware.py
+++ b/scrapy_autounit/middleware.py
@@ -4,6 +4,7 @@ from pathlib import Path
 
 from scrapy.http import Request
 from scrapy.exceptions import NotConfigured
+from scrapy.commands.genspider import sanitize_module_name
 
 from .utils import (
     add_sample,
@@ -26,6 +27,13 @@ def _copy_settings(settings):
 
 class AutounitMiddleware:
     def __init__(self, settings):
+        if not any(
+            self.__class__.__name__ in s
+            for s in settings.getwithbase('SPIDER_MIDDLEWARES').keys()
+        ):
+            raise ValueError(
+                '%s must be in SPIDER_MIDDLEWARES' % (
+                    self.__class__.__name__,))
         if not settings.getbool('AUTOUNIT_ENABLED'):
             raise NotConfigured('scrapy-autounit is not enabled')
 
@@ -91,7 +99,7 @@ class AutounitMiddleware:
 
         test_dir, test_name = get_or_create_test_dir(
             self.base_path,
-            spider.name,
+            sanitize_module_name(spider.name),
             callback_name,
             settings.get('AUTOUNIT_EXTRA_PATH'),
         )

Warn if started as DOWNLOADER_MIDDLEWARE instead of SPIDER_MIDDLEWARE
diff --git a/tests/test_record.py b/tests/test_record.py
index a4865ad..33ed48e 100644
--- a/tests/test_record.py
+++ b/tests/test_record.py
@@ -10,7 +10,7 @@ import scrapy
 
 
 class MySpider(scrapy.Spider):
-    name = 'myspider'
+    name = '{name}'
 
     custom_settings = dict(
         SPIDER_MIDDLEWARES={{
@@ -66,6 +66,7 @@ class CaseSpider(object):
             dest.write('SPIDER_MODULES = ["myproject"]\n')
         self._start_requests = None
         self._parse = None
+        self._spider_name = 'myspider'
 
     def __enter__(self):
         return self
@@ -73,17 +74,19 @@ class CaseSpider(object):
     def __exit__(self, exc_type, exc_value, traceback):
         shutil.rmtree(self.dir)
 
+    def name(self, string):
+        self._spider_name = string
+
     def start_requests(self, string):
         self._start_requests = string
-        self._write_spider()
 
     def parse(self, string):
         self._parse = string
-        self._write_spider()
 
     def _write_spider(self):
         with open(os.path.join(self.proj_dir, 'myspider.py'), 'w') as dest:
             dest.write(SPIDER_TEMPLATE.format(
+                name=self._spider_name,
                 start_requests=self._start_requests,
                 parse=self._parse
             ))
@@ -91,11 +94,12 @@ class CaseSpider(object):
     def record(self, args=None, settings=None):
         if self._start_requests is None or self._parse is None:
             raise AssertionError()
+        self._write_spider()
         env = os.environ.copy()
         env['PYTHONPATH'] = self.dir  # doesn't work if == cwd
         env['SCRAPY_SETTINGS_MODULE'] = 'myproject.settings'
         command_args = [
-            'scrapy', 'crawl', 'myspider',
+            'scrapy', 'crawl', self._spider_name,
             '-s', 'AUTOUNIT_ENABLED=1',
         ]
         for k, v in (args or {}).items():
@@ -160,3 +164,18 @@ class TestRecording(unittest.TestCase):
             ''')
             spider.record(settings=dict(AUTOUNIT_EXTRA_PATH='abc'))
             spider.test()
+
+    def test_empty(self):
+        with CaseSpider() as spider:
+            spider.start_requests("yield scrapy.Request('data:text/plain,')")
+            spider.parse('pass')
+            spider.record()
+            spider.test()
+
+    def test_dotted_name(self):
+        with CaseSpider() as spider:
+            spider.name('my.spider')
+            spider.start_requests("yield scrapy.Request('data:text/plain,')")
+            spider.parse('pass')
+            spider.record()
+            spider.test()

pip install -e .
parse_log_pytest
scrapy pytest
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
Automat==22.10.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
constantly==15.1.0
cryptography==40.0.2
cssselect==1.1.0
filelock==3.4.1
hyperlink==21.0.0
idna==3.10
importlib-metadata==4.8.3
incremental==22.10.0
iniconfig==1.1.1
itemadapter==0.7.0
itemloaders==1.0.6
jmespath==0.10.0
lxml==5.3.1
packaging==21.3
parsel==1.6.0
pathlib==1.0.1
pluggy==1.0.0
Protego==0.2.1
py==1.11.0
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycparser==2.21
PyDispatcher==2.0.7
pyOpenSSL==23.2.0
pyparsing==3.1.4
pytest==7.0.1
queuelib==1.6.2
requests==2.27.1
requests-file==2.1.0
Scrapy==2.6.3
-e git+https://github.com/scrapinghub/scrapy-autounit.git@99d20cf37b1157b7efecfa60510ef3c42dc35603#egg=scrapy_autounit
service-identity==21.1.0
six==1.17.0
tldextract==3.1.2
tomli==1.2.3
Twisted==22.4.0
typing_extensions==4.1.1
urllib3==1.26.20
w3lib==2.0.1
zipp==3.6.0
zope.interface==5.5.2

name: scrapy-autounit
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - automat==22.10.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - constantly==15.1.0
      - cryptography==40.0.2
      - cssselect==1.1.0
      - filelock==3.4.1
      - hyperlink==21.0.0
      - idna==3.10
      - importlib-metadata==4.8.3
      - incremental==22.10.0
      - iniconfig==1.1.1
      - itemadapter==0.7.0
      - itemloaders==1.0.6
      - jmespath==0.10.0
      - lxml==5.3.1
      - packaging==21.3
      - parsel==1.6.0
      - pathlib==1.0.1
      - pluggy==1.0.0
      - protego==0.2.1
      - py==1.11.0
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pycparser==2.21
      - pydispatcher==2.0.7
      - pyopenssl==23.2.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - queuelib==1.6.2
      - requests==2.27.1
      - requests-file==2.1.0
      - scrapy==2.6.3
      - service-identity==21.1.0
      - six==1.17.0
      - tldextract==3.1.2
      - tomli==1.2.3
      - twisted==22.4.0
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - w3lib==2.0.1
      - zipp==3.6.0
      - zope-interface==5.5.2
prefix: /opt/conda/envs/scrapy-autounit

tests/test_record.py::TestRecording::test_dotted_name
tests/test_record.py::TestRecording::test_empty tests/test_record.py::TestRecording::test_normal tests/test_record.py::TestRecording::test_path_extra
scrapy_autounit/middleware.py
scrapy_autounit/middleware.py