diff --git a/src/pypelines/__init__.py b/src/pypelines/__init__.py
index 0f49f0c017c872af43574f59c416d888e215ee26..c488c86fa35291fea2c19cf2eaafec35cc08c773 100644
--- a/src/pypelines/__init__.py
+++ b/src/pypelines/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "0.0.70"
+__version__ = "0.0.71"
 
 from . import loggs
 from .pipes import *
diff --git a/src/pypelines/pipes.py b/src/pypelines/pipes.py
index bce4e8a4d234614815f6fd3d6f710a1398bfecd0..74ed279c5bf4fa9ac5ae73f2e6ebacc2d0de4ed6 100644
--- a/src/pypelines/pipes.py
+++ b/src/pypelines/pipes.py
@@ -2,6 +2,7 @@ from .steps import BaseStep
 from .multisession import BaseMultisessionAccessor
 from .sessions import Session
 from .disk import BaseDiskObject
+from .utils import to_snake_case
 
 from functools import wraps
 import inspect, hashlib
@@ -13,6 +14,7 @@ from abc import ABCMeta, abstractmethod
 from typing import Callable, Type, Iterable, Protocol, TYPE_CHECKING, Literal, Dict
 from types import MethodType
 
+
 if TYPE_CHECKING:
     from .pipelines import Pipeline
 
@@ -65,7 +67,7 @@ class BasePipe(BasePipeType, metaclass=ABCMeta):
             None
         """
         self.pipeline = parent_pipeline
-        self.pipe_name = self.__class__.__name__
+        self.pipe_name = to_snake_case(self.__class__.__name__)
 
         _steps: Dict[str, MethodType] = {}
 
@@ -84,6 +86,7 @@ class BasePipe(BasePipeType, metaclass=ABCMeta):
         for step_name, step in steps_members_scanner:
             print("step:", step_name)
             if not requires_is_step_attr or getattr(step, "is_step", False):
+                step_name = to_snake_case(step_name)
                 _steps[step_name] = step
 
         if len(_steps) < 1:
diff --git a/src/pypelines/steps.py b/src/pypelines/steps.py
index 7b3174456117f358cd7819e0b792ed328c4c5436..5d6f0b69700033473cd8ae416a7cd40842e6e15c 100644
--- a/src/pypelines/steps.py
+++ b/src/pypelines/steps.py
@@ -1,6 +1,7 @@
 from functools import wraps, partial, update_wrapper
 from .loggs import loggedmethod, NAMELENGTH
 from .arguments import autoload_arguments
+from .utils import to_snake_case
 
 import logging, inspect
 from pandas import DataFrame
@@ -56,7 +57,7 @@ def stepmethod(requires=[], version=None, do_dispatch=True, on_save_callbacks=[]
         function.is_step = True
         function.version = version
         function.do_dispatch = do_dispatch
-        function.step_name = function.__name__
+        function.step_name = to_snake_case(function.__name__)
         function.callbacks = [on_save_callbacks] if not isinstance(on_save_callbacks, list) else on_save_callbacks
         return function
 
@@ -77,7 +78,7 @@ class BaseStep:
     pipe: "BasePipe"
     pipeline: "Pipeline"
 
-    def __init__(self, pipeline: "Pipeline", pipe: "BasePipe", worker: MethodType, step_name=None):
+    def __init__(self, pipeline: "Pipeline", pipe: "BasePipe", worker: MethodType, step_name: str = ""):
         """Initialize a BaseStep object.
 
         Args:
@@ -109,7 +110,7 @@ class BaseStep:
         self.do_dispatch = getattr(self.worker, "do_dispatch", False)
         self.version = getattr(self.worker, "version", 0)
         self.requires = getattr(self.worker, "requires", [])
-        self.step_name = getattr(self.worker, "step_name", step_name)
+        self.step_name = to_snake_case(getattr(self.worker, "step_name", step_name))
 
         if not self.step_name:
             raise ValueError("Step name cannot be blank nor None")
diff --git a/src/pypelines/utils.py b/src/pypelines/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2820c1e7c60f6af93c14f37356f01fd4947788b
--- /dev/null
+++ b/src/pypelines/utils.py
@@ -0,0 +1,10 @@
+import re
+
+
+def to_snake_case(text):
+    # Replace spaces or hyphens with underscores
+    text = re.sub(r"[\s-]+", "_", text)
+    # Convert CamelCase to snake_case
+    text = re.sub(r"([a-z])([A-Z])", r"\1_\2", text)
+    # Convert all characters to lowercase
+    return text.lower()
diff --git a/tests/test_core.py b/tests/test_core.py
index ab56a380c8ae445718c670057d98a2741883e026..572cd4f33879e6dffec671468a48cdaae1d18415 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -13,38 +13,97 @@ from pypelines.sessions import Session
 from pypelines import Pipeline, stepmethod, BaseStep
 from pypelines.pickle_backend import PicklePipe
 
+from pathlib import Path
+
 
 @pytest.fixture
-def test_class_based_pypeline():
+def pipeline_steps_group_class_based():
 
-    pipeline = Pipeline("test_class_based")
+    test_pipeline = Pipeline("test_class_based")
 
-    @pipeline.register_pipe
+    @test_pipeline.register_pipe
     class MyPipe(PicklePipe):
         class Steps:
             def my_step(self, session, extra=""):
-                return 1
+                return "a_good_result"
 
             my_step.requires = []
 
-    return pipeline
+    return test_pipeline
 
 
 @pytest.fixture
-def test_method_based_pypeline():
+def pipeline_method_based():
 
-    pipeline = Pipeline("test_method_based")
+    test_pipeline = Pipeline("test_method_based")
 
-    @pipeline.register_pipe
+    @test_pipeline.register_pipe
     class MyPipe(PicklePipe):
 
         @stepmethod(requires=[])
         def my_step(self, session, extra=""):
-            return 1
+            return "a_good_result"
+
+    return test_pipeline
+
+
+def get_pipelines_fixtures():
+    return ["pipeline_method_based", "pipeline_steps_group_class_based"]
+
+
+@pytest.fixture
+def session_root_path():
+    directory = Path("./tests/temp_sessions_directory")
+    directory.mkdir(parents=True, exist_ok=True)
+    yield directory
+
+    if directory.exists():
+
+        def remove_directory(path: Path):
+            print("removing :", path)
+            for child in path.iterdir():
+                if child.is_file():
+                    child.unlink()
+                else:
+                    remove_directory(child)
+            path.rmdir()
+
+        remove_directory(directory)
+
+
+@pytest.fixture
+def session(session_root_path):
+    test_session = Session(subject="test_subject", date="2024-10-05", number=1, auto_path=True, path=session_root_path)
+    return test_session
+
+
+@pytest.mark.parametrize("pipeline_fixture_name", get_pipelines_fixtures())
+def test_pypeline_creation(request, pipeline_fixture_name):
+    pipeline = request.getfixturevalue(pipeline_fixture_name)
+
+    assert isinstance(pipeline.my_pipe.my_step, BaseStep)
+    assert hasattr(pipeline.my_pipe.my_step, "generate")
+    assert hasattr(pipeline.my_pipe.my_step, "load")
+    assert hasattr(pipeline.my_pipe.my_step, "save")
+
+
+@pytest.mark.parametrize("pipeline_fixture_name", get_pipelines_fixtures())
+def test_pypeline_call(request, pipeline_fixture_name: str, session):
+    pipeline = request.getfixturevalue(pipeline_fixture_name)
+
+    # expecting the output to not be present if the pipeline step was not generated first
+    with pytest.raises(ValueError):
+        assert pipeline.my_pipe.my_step.load(session) == "a_good_result"
+
+    # this only calculates and returns the pipeline step output, and do not saves it
+    assert pipeline.my_pipe.my_step(session) == "a_good_result"
+
+    # expecting the output to not be present if the pipeline step was not generated first
+    with pytest.raises(ValueError):
+        assert pipeline.my_pipe.my_step.load(session) == "a_good_result"
 
+    # generate the pipeline step output to file (saves it with generation mechanism)
+    assert pipeline.my_pipe.my_step.generate(session) == "a_good_result"
 
-def test_pypeline_creation(test_class_based_pypeline):
-    assert isinstance(test_class_based_pypeline.MyPipe.my_step, BaseStep)
-    assert hasattr(test_class_based_pypeline.MyPipe.my_step, "generate")
-    assert hasattr(test_class_based_pypeline.MyPipe.my_step, "load")
-    assert hasattr(test_class_based_pypeline.MyPipe.my_step, "save")
+    # expecting the output to be present now
+    assert pipeline.my_pipe.my_step.load(session) == "a_good_result"