Browse Source

refactor excel dn write function

Toan Quach 1 year ago
parent
commit
65020a27ae

+ 5 - 7
taipy/core/_orchestrator/_orchestrator.py

@@ -96,7 +96,7 @@ class _Orchestrator(_AbstractOrchestrator):
             cls._check_and_execute_jobs_if_development_mode()
         else:
             if wait:
-                cls._wait_until_job_finished(jobs, timeout=timeout)
+                cls._wait_until_job_finished(jobs, timeout=timeout or 0)
         return submission
 
     @classmethod
@@ -143,7 +143,7 @@ class _Orchestrator(_AbstractOrchestrator):
             cls._check_and_execute_jobs_if_development_mode()
         else:
             if wait:
-                cls._wait_until_job_finished(job, timeout=timeout)
+                cls._wait_until_job_finished(job, timeout=timeout or 0)
         return submission
 
     @classmethod
@@ -185,12 +185,10 @@ class _Orchestrator(_AbstractOrchestrator):
             cls.jobs_to_run.put(job)
 
     @classmethod
-    def _wait_until_job_finished(cls, jobs: Union[List[Job], Job], timeout: Optional[Union[float, int]] = None):
+    def _wait_until_job_finished(cls, jobs: Union[List[Job], Job], timeout: float = 0):
         #  Note: this method should be prefixed by two underscores, but it has only one, so it can be mocked in tests.
         def __check_if_timeout(st, to):
-            if to:
-                return (datetime.now() - st).seconds < to
-            return True
+            return (datetime.now() - st).seconds < to
 
         start = datetime.now()
         jobs = list(jobs) if isinstance(jobs, Iterable) else [jobs]
@@ -286,7 +284,7 @@ class _Orchestrator(_AbstractOrchestrator):
 
     @classmethod
     def __remove_jobs_to_run(cls, jobs):
-        new_jobs_to_run: Queue = Queue()
+        new_jobs_to_run = Queue()
         while not cls.jobs_to_run.empty():
             current_job = cls.jobs_to_run.get()
             if current_job not in jobs:

+ 16 - 0
taipy/core/data/_abstract_tabular.py

@@ -9,6 +9,12 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
+from dataclasses import asdict, is_dataclass
+from typing import Any, Union
+
+import numpy as np
+import pandas as pd
+
 from ..exceptions.exceptions import InvalidExposedType
 
 
@@ -22,6 +28,16 @@ class _AbstractTabularDataNode(object):
     _EXPOSED_TYPE_MODIN = "modin"  # Deprecated in favor of pandas since 3.1.0
     __VALID_STRING_EXPOSED_TYPES = [_EXPOSED_TYPE_PANDAS, _EXPOSED_TYPE_NUMPY]
 
+    def _convert_data_to_dataframe(self, exposed_type: Any, data: Any) -> Union[pd.DataFrame, pd.Series]:
+        if exposed_type == self._EXPOSED_TYPE_PANDAS and isinstance(data, (pd.DataFrame, pd.Series)):
+            return data
+        elif exposed_type == self._EXPOSED_TYPE_NUMPY and isinstance(data, np.ndarray):
+            return pd.DataFrame(data)
+        elif isinstance(data, list) and not isinstance(exposed_type, str):
+            if all(is_dataclass(row) for row in data):
+                return pd.DataFrame.from_records([asdict(row) for row in data])
+        return pd.DataFrame(data)
+
     @staticmethod
     def _check_exposed_type(exposed_type):
         valid_string_exposed_types = _AbstractTabularDataNode.__VALID_STRING_EXPOSED_TYPES

+ 4 - 19
taipy/core/data/csv.py

@@ -11,7 +11,6 @@
 
 import csv
 import os
-from dataclasses import asdict, is_dataclass
 from datetime import datetime, timedelta
 from os.path import isfile
 from typing import Any, Dict, List, Optional, Set
@@ -185,9 +184,7 @@ class CSVDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):
                 for line in reader:
                     res.append(custom_class(**line))
             else:
-                reader = csv.reader(
-                    csvFile,
-                )
+                reader = csv.reader(csvFile)
                 for line in reader:
                     res.append(custom_class(*line))
             return res
@@ -220,26 +217,14 @@ class CSVDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):
                 self._path, mode="a", index=False, encoding=self.properties[self.__ENCODING_KEY], header=False
             )
 
-    def __convert_data_to_dataframe(self, data: Any):
-        exposed_type = self.properties[self._EXPOSED_TYPE_PROPERTY]
-        if exposed_type == self._EXPOSED_TYPE_PANDAS and isinstance(data, (pd.DataFrame, pd.Series)):
-            return data
-        elif exposed_type == self._EXPOSED_TYPE_NUMPY and isinstance(data, np.ndarray):
-            return pd.DataFrame(data)
-        elif isinstance(data, list) and not isinstance(exposed_type, str):
-            if all(is_dataclass(row) for row in data):
-                return pd.DataFrame.from_records([asdict(row) for row in data])
-            # return pd.DataFrame.from_records([row.to_dict() for row in data])
-        else:
-            return pd.DataFrame(data)
-
     def _write(self, data: Any):
+        exposed_type = self.properties[self._EXPOSED_TYPE_PROPERTY]
         if self.properties[self.__HAS_HEADER_PROPERTY]:
-            self.__convert_data_to_dataframe(data).to_csv(
+            self._convert_data_to_dataframe(exposed_type, data).to_csv(
                 self._path, index=False, encoding=self.properties[self.__ENCODING_KEY]
             )
         else:
-            self.__convert_data_to_dataframe(data).to_csv(
+            self._convert_data_to_dataframe(exposed_type, data).to_csv(
                 self._path, index=False, encoding=self.properties[self.__ENCODING_KEY], header=None
             )
 

+ 6 - 22
taipy/core/data/excel.py

@@ -263,7 +263,7 @@ class ExcelDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):
         return pd.read_excel(self._path, sheet_name=sheet_names, **kwargs)
 
     def __get_sheet_names_and_header(self, sheet_names):
-        kwargs: Dict[str, Any] = {}
+        kwargs = {}
         if sheet_names is None:
             sheet_names = self.properties[self.__SHEET_NAME_PROPERTY]
         if not self.properties[self.__HAS_HEADER_PROPERTY]:
@@ -331,10 +331,7 @@ class ExcelDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):
         with pd.ExcelWriter(self._path) as writer:
             # Each key stands for a sheet name
             for key in data.keys():
-                if isinstance(data[key], np.ndarray):
-                    df = pd.DataFrame(data[key])
-                else:
-                    df = data[key]
+                df = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data[key])
 
                 if columns:
                     data[key].columns = columns
@@ -342,24 +339,11 @@ class ExcelDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):
                 df.to_excel(writer, key, index=False)
 
     def _write(self, data: Any):
-        # TODO:
-        # if dict -> multi sheet
-        # 1 pandas as child
-        # 2 numpy as child
-        # 3 custom as child
-
-        # if pandas
-
-        # if numpy
-
-        # if custom exposed type
-
-        if isinstance(data, Dict) and all(isinstance(x, (pd.DataFrame, np.ndarray)) for x in data.values()):
-            self.__write_excel_with_multiple_sheets(data)
-        elif isinstance(data, pd.DataFrame):
-            self.__write_excel_with_single_sheet(data.to_excel, self._path, index=False)
+        if isinstance(data, Dict):
+            return self.__write_excel_with_multiple_sheets(data)
         else:
-            self.__write_excel_with_single_sheet(pd.DataFrame(data).to_excel, self._path, index=False)
+            data = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data)
+            self.__write_excel_with_single_sheet(data.to_excel, self._path, index=False)
 
     def write_with_column_names(self, data: Any, columns: List[str] = None, job_id: Optional[JobId] = None):
         """Write a set of columns.

+ 7 - 4
taipy/core/data/parquet.py

@@ -244,10 +244,13 @@ class ParquetDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode)
         }
         kwargs.update(self.properties[self.__WRITE_KWARGS_PROPERTY])
         kwargs.update(write_kwargs)
-        if isinstance(data, pd.DataFrame):
-            data.to_parquet(self._path, **kwargs)
-        else:
-            pd.DataFrame(data).to_parquet(self._path, **kwargs)
+
+        # TODO: test this new code
+        data = self._convert_data_to_dataframe(self.properties[self._EXPOSED_TYPE_PROPERTY], data)
+        if isinstance(data, pd.Series):
+            data = pd.DataFrame(data)
+        data.to_parquet(self._path, **kwargs)
+
         self.track_edit(timestamp=datetime.now(), job_id=job_id)
 
     def read_with_kwargs(self, **read_kwargs):

+ 15 - 24
tests/core/data/test_read_csv_data_node.py

@@ -21,7 +21,7 @@ from taipy.config.common.scope import Scope
 from taipy.core.data.csv import CSVDataNode
 from taipy.core.exceptions.exceptions import NoData
 
-DATA_SAMPLE_PATH = "data_sample/example.csv"
+csv_file_path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.csv")
 
 
 @dataclasses.dataclass
@@ -39,33 +39,28 @@ def test_raise_no_data_with_header():
 
 
 def test_read_with_header_pandas():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-
-    csv_data_node_as_pandas = CSVDataNode("bar", Scope.SCENARIO, properties={"path": path})
+    csv_data_node_as_pandas = CSVDataNode("bar", Scope.SCENARIO, properties={"path": csv_file_path})
     data_pandas = csv_data_node_as_pandas.read()
     assert isinstance(data_pandas, pd.DataFrame)
     assert len(data_pandas) == 10
-    assert np.array_equal(data_pandas.to_numpy(), pd.read_csv(path).to_numpy())
+    assert pd.DataFrame.equals(data_pandas, pd.read_csv(csv_file_path))
 
 
 def test_read_with_header_numpy():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-
     csv_data_node_as_numpy = CSVDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "has_header": True, "exposed_type": "numpy"}
+        "bar", Scope.SCENARIO, properties={"path": csv_file_path, "has_header": True, "exposed_type": "numpy"}
     )
     data_numpy = csv_data_node_as_numpy.read()
     assert isinstance(data_numpy, np.ndarray)
     assert len(data_numpy) == 10
-    assert np.array_equal(data_numpy, pd.read_csv(path).to_numpy())
+    assert np.array_equal(data_numpy, pd.read_csv(csv_file_path).to_numpy())
 
 
 def test_read_with_header_custom_exposed_type():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-    data_pandas = pd.read_csv(path)
+    data_pandas = pd.read_csv(csv_file_path)
 
     csv_data_node_as_custom_object = CSVDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "exposed_type": MyCustomObject}
+        "bar", Scope.SCENARIO, properties={"path": csv_file_path, "exposed_type": MyCustomObject}
     )
     data_custom = csv_data_node_as_custom_object.read()
     assert isinstance(data_custom, list)
@@ -86,38 +81,34 @@ def test_raise_no_data_without_header():
 
 
 def test_read_without_header_pandas():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-
-    csv_data_node_as_pandas = CSVDataNode("bar", Scope.SCENARIO, properties={"path": path, "has_header": False})
+    csv_data_node_as_pandas = CSVDataNode(
+        "bar", Scope.SCENARIO, properties={"path": csv_file_path, "has_header": False}
+    )
     data_pandas = csv_data_node_as_pandas.read()
     assert isinstance(data_pandas, pd.DataFrame)
     assert len(data_pandas) == 11
-    assert np.array_equal(data_pandas.to_numpy(), pd.read_csv(path, header=None).to_numpy())
+    assert pd.DataFrame.equals(data_pandas, pd.read_csv(csv_file_path, header=None))
 
 
 def test_read_without_header_numpy():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-
     csv_data_node_as_numpy = CSVDataNode(
-        "qux", Scope.SCENARIO, properties={"path": path, "has_header": False, "exposed_type": "numpy"}
+        "qux", Scope.SCENARIO, properties={"path": csv_file_path, "has_header": False, "exposed_type": "numpy"}
     )
     data_numpy = csv_data_node_as_numpy.read()
     assert isinstance(data_numpy, np.ndarray)
     assert len(data_numpy) == 11
-    assert np.array_equal(data_numpy, pd.read_csv(path, header=None).to_numpy())
+    assert np.array_equal(data_numpy, pd.read_csv(csv_file_path, header=None).to_numpy())
 
 
 def test_read_without_header_custom_exposed_type():
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), DATA_SAMPLE_PATH)
-
     csv_data_node_as_custom_object = CSVDataNode(
-        "quux", Scope.SCENARIO, properties={"path": path, "has_header": False, "exposed_type": MyCustomObject}
+        "quux", Scope.SCENARIO, properties={"path": csv_file_path, "has_header": False, "exposed_type": MyCustomObject}
     )
     data_custom = csv_data_node_as_custom_object.read()
     assert isinstance(data_custom, list)
     assert len(data_custom) == 11
 
-    data_pandas = pd.read_csv(path, header=None)
+    data_pandas = pd.read_csv(csv_file_path, header=None)
     for (_, row_pandas), row_custom in zip(data_pandas.iterrows(), data_custom):
         assert isinstance(row_custom, MyCustomObject)
         assert row_pandas[0] == row_custom.id

+ 217 - 140
tests/core/data/test_read_excel_data_node.py

@@ -55,59 +55,126 @@ class MyCustomObject2:
         self.text = text
 
 
-def test_read_with_header():
+excel_file_path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
+sheet_names = ["Sheet1", "Sheet2"]
+custom_class_dict = {"Sheet1": MyCustomObject1, "Sheet2": MyCustomObject2}
+
+
+def test_raise_no_data_with_header():
     with pytest.raises(NoData):
         not_existing_excel = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": "WRONG.xlsx"})
         assert not_existing_excel.read() is None
         not_existing_excel.read_or_raise()
 
+
+def test_read_empty_excel_with_header():
     empty_excel_path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/empty.xlsx")
     empty_excel = ExcelDataNode(
         "foo",
         Scope.SCENARIO,
-        properties={"path": empty_excel_path, "exposed_type": MyCustomObject, "has_header": True},
+        properties={"path": empty_excel_path, "exposed_type": MyCustomObject},
     )
     assert len(empty_excel.read()) == 0
 
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
 
-    # Create ExcelDataNode without exposed_type (Default is pandas.DataFrame)
-    excel_data_node_as_pandas = ExcelDataNode("bar", Scope.SCENARIO, properties={"path": path, "sheet_name": "Sheet1"})
+def test_raise_no_data_without_header():
+    with pytest.raises(NoData):
+        not_existing_excel = ExcelDataNode(
+            "foo", Scope.SCENARIO, properties={"path": "WRONG.xlsx", "has_header": False}
+        )
+        assert not_existing_excel.read() is None
+        not_existing_excel.read_or_raise()
+
+
+def test_read_empty_excel_without_header():
+    empty_excel_path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/empty.xlsx")
+    empty_excel = ExcelDataNode(
+        "foo",
+        Scope.SCENARIO,
+        properties={"path": empty_excel_path, "exposed_type": MyCustomObject, "has_header": False},
+    )
+    assert len(empty_excel.read()) == 0
+
+
+def test_read_multi_sheet_with_header_no_data():
+    not_existing_excel = ExcelDataNode(
+        "foo",
+        Scope.SCENARIO,
+        properties={"path": "WRONG.xlsx", "sheet_name": ["sheet_name_1", "sheet_name_2"]},
+    )
+    with pytest.raises(NoData):
+        assert not_existing_excel.read() is None
+        not_existing_excel.read_or_raise()
+
+
+def test_read_multi_sheet_without_header_no_data():
+    not_existing_excel = ExcelDataNode(
+        "foo",
+        Scope.SCENARIO,
+        properties={"path": "WRONG.xlsx", "has_header": False, "sheet_name": ["sheet_name_1", "sheet_name_2"]},
+    )
+    with pytest.raises(NoData):
+        assert not_existing_excel.read() is None
+        not_existing_excel.read_or_raise()
+
+
+########################## SINGLE SHEET ##########################
+
+
+def test_read_single_sheet_with_header_no_existing_sheet():
+    non_existing_sheet_name_custom = ExcelDataNode(
+        "bar",
+        Scope.SCENARIO,
+        properties={"path": excel_file_path, "sheet_name": "abc", "exposed_type": MyCustomObject},
+    )
+    with pytest.raises(NonExistingExcelSheet):
+        non_existing_sheet_name_custom.read()
+
+
+def test_read_single_sheet_without_header_no_existing_sheet():
+    non_existing_sheet_name_custom = ExcelDataNode(
+        "bar",
+        Scope.SCENARIO,
+        properties={"path": excel_file_path, "has_header": False, "sheet_name": "abc", "exposed_type": MyCustomObject},
+    )
+    with pytest.raises(NonExistingExcelSheet):
+        non_existing_sheet_name_custom.read()
+
+
+def test_read_with_header_pandas():
+    excel_data_node_as_pandas = ExcelDataNode(
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "sheet_name": "Sheet1"}
+    )
 
     data_pandas = excel_data_node_as_pandas.read()
     assert isinstance(data_pandas, pd.DataFrame)
     assert len(data_pandas) == 5
-    assert np.array_equal(data_pandas.to_numpy(), pd.read_excel(path).to_numpy())
+    assert pd.DataFrame.equals(data_pandas, pd.read_excel(excel_file_path))
+
 
-    # Create ExcelDataNode with numpy exposed_type
+def test_read_with_header_numpy():
     excel_data_node_as_numpy = ExcelDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "exposed_type": "numpy", "sheet_name": "Sheet1"}
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "exposed_type": "numpy", "sheet_name": "Sheet1"}
     )
 
     data_numpy = excel_data_node_as_numpy.read()
     assert isinstance(data_numpy, np.ndarray)
     assert len(data_numpy) == 5
-    assert np.array_equal(data_numpy, pd.read_excel(path).to_numpy())
+    assert np.array_equal(data_numpy, pd.read_excel(excel_file_path).to_numpy())
 
-    # Create the same ExcelDataNode but with custom exposed_type
-    non_existing_sheet_name_custom = ExcelDataNode(
-        "bar",
-        Scope.SCENARIO,
-        properties={"path": path, "sheet_name": "abc", "exposed_type": MyCustomObject},
-    )
-    with pytest.raises(NonExistingExcelSheet):
-        non_existing_sheet_name_custom.read()
 
+def test_read_with_header_custom_exposed_type():
     excel_data_node_as_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "exposed_type": MyCustomObject, "sheet_name": "Sheet1"},
+        properties={"path": excel_file_path, "exposed_type": MyCustomObject, "sheet_name": "Sheet1"},
     )
 
     data_custom = excel_data_node_as_custom_object.read()
     assert isinstance(data_custom, list)
     assert len(data_custom) == 5
 
+    data_pandas = pd.read_excel(excel_file_path)
     for (_, row_pandas), row_custom in zip(data_pandas.iterrows(), data_custom):
         assert isinstance(row_custom, MyCustomObject)
         assert row_pandas["id"] == row_custom.id
@@ -115,49 +182,35 @@ def test_read_with_header():
         assert row_pandas["text"] == row_custom.text
 
 
-def test_read_without_header():
-    not_existing_excel = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": "WRONG.xlsx", "has_header": False})
-    with pytest.raises(NoData):
-        assert not_existing_excel.read() is None
-        not_existing_excel.read_or_raise()
-
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
-
-    # Create ExcelDataNode without exposed_type (Default is pandas.DataFrame)
+def test_read_without_header_pandas():
     excel_data_node_as_pandas = ExcelDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "has_header": False, "sheet_name": "Sheet1"}
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "has_header": False, "sheet_name": "Sheet1"}
     )
     data_pandas = excel_data_node_as_pandas.read()
     assert isinstance(data_pandas, pd.DataFrame)
     assert len(data_pandas) == 6
-    assert np.array_equal(data_pandas.to_numpy(), pd.read_excel(path, header=None).to_numpy())
+    assert pd.DataFrame.equals(data_pandas, pd.read_excel(excel_file_path, header=None))
+
 
-    # Create ExcelDataNode with numpy exposed_type
+def test_read_without_header_numpy():
     excel_data_node_as_numpy = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "exposed_type": "numpy", "sheet_name": "Sheet1"},
+        properties={"path": excel_file_path, "has_header": False, "exposed_type": "numpy", "sheet_name": "Sheet1"},
     )
 
     data_numpy = excel_data_node_as_numpy.read()
     assert isinstance(data_numpy, np.ndarray)
     assert len(data_numpy) == 6
-    assert np.array_equal(data_numpy, pd.read_excel(path, header=None).to_numpy())
+    assert np.array_equal(data_numpy, pd.read_excel(excel_file_path, header=None).to_numpy())
 
-    # Create the same ExcelDataNode but with custom exposed_type
-    non_existing_sheet_name_custom = ExcelDataNode(
-        "bar",
-        Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "sheet_name": "abc", "exposed_type": MyCustomObject},
-    )
-    with pytest.raises(NonExistingExcelSheet):
-        non_existing_sheet_name_custom.read()
 
+def test_read_without_header_exposed_custom_type():
     excel_data_node_as_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
         properties={
-            "path": path,
+            "path": excel_file_path,
             "has_header": False,
             "exposed_type": MyCustomObject,
             "sheet_name": "Sheet1",
@@ -168,6 +221,7 @@ def test_read_without_header():
     assert isinstance(data_custom, list)
     assert len(data_custom) == 6
 
+    data_pandas = pd.read_excel(excel_file_path, header=None)
     for (_, row_pandas), row_custom in zip(data_pandas.iterrows(), data_custom):
         assert isinstance(row_custom, MyCustomObject)
         assert row_pandas[0] == row_custom.id
@@ -175,22 +229,71 @@ def test_read_without_header():
         assert row_pandas[2] == row_custom.text
 
 
-def test_read_multi_sheet_with_header():
-    not_existing_excel = ExcelDataNode(
-        "foo",
+########################## MULTI SHEET ##########################
+
+
+def test_read_multi_sheet_with_header_no_existing_sheet():
+    non_existing_sheet_name_custom = ExcelDataNode(
+        "bar",
         Scope.SCENARIO,
-        properties={"path": "WRONG.xlsx", "sheet_name": ["sheet_name_1", "sheet_name_2"]},
+        properties={
+            "path": excel_file_path,
+            "sheet_name": ["Sheet1", "xyz"],
+            "exposed_type": MyCustomObject1,
+        },
     )
-    with pytest.raises(NoData):
-        assert not_existing_excel.read() is None
-        not_existing_excel.read_or_raise()
+    with pytest.raises(NonExistingExcelSheet):
+        non_existing_sheet_name_custom.read()
+
+
+def test_read_multi_sheet_without_header_no_existing_sheet():
+    non_existing_sheet_name_custom = ExcelDataNode(
+        "bar",
+        Scope.SCENARIO,
+        properties={
+            "path": excel_file_path,
+            "has_header": False,
+            "sheet_name": ["Sheet1", "xyz"],
+            "exposed_type": MyCustomObject1,
+        },
+    )
+    with pytest.raises(NonExistingExcelSheet):
+        non_existing_sheet_name_custom.read()
 
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
-    sheet_names = ["Sheet1", "Sheet2"]
 
-    # Create ExcelDataNode without exposed_type (Default is pandas.DataFrame)
+def test_raise_exposed_type_length_mismatch_with_header():
+    with pytest.raises(ExposedTypeLengthMismatch):
+        dn = ExcelDataNode(
+            "bar",
+            Scope.SCENARIO,
+            properties={
+                "path": excel_file_path,
+                "sheet_name": ["Sheet1"],
+                "exposed_type": [MyCustomObject1, MyCustomObject2],
+            },
+        )
+        dn.read()
+
+
+def test_raise_exposed_type_length_mismatch_without_header():
+    with pytest.raises(ExposedTypeLengthMismatch):
+        dn = ExcelDataNode(
+            "bar",
+            Scope.SCENARIO,
+            properties={
+                "path": excel_file_path,
+                "sheet_name": ["Sheet1"],
+                "exposed_type": [MyCustomObject1, MyCustomObject2],
+                "has_header": False,
+            },
+        )
+        dn.read()
+
+
+def test_read_multi_sheet_with_header_pandas():
+    # With sheet name
     excel_data_node_as_pandas = ExcelDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "sheet_name": sheet_names}
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "sheet_name": sheet_names}
     )
 
     data_pandas = excel_data_node_as_pandas.read()
@@ -202,9 +305,10 @@ def test_read_multi_sheet_with_header():
     )
     assert list(data_pandas.keys()) == sheet_names
     for sheet_name in sheet_names:
-        assert data_pandas[sheet_name].equals(pd.read_excel(path, sheet_name=sheet_name))
+        assert pd.DataFrame.equals(data_pandas[sheet_name], pd.read_excel(excel_file_path, sheet_name=sheet_name))
 
-    excel_data_node_as_pandas_no_sheet_name = ExcelDataNode("bar", Scope.SCENARIO, properties={"path": path})
+    # Without sheet name
+    excel_data_node_as_pandas_no_sheet_name = ExcelDataNode("bar", Scope.SCENARIO, properties={"path": excel_file_path})
 
     data_pandas_no_sheet_name = excel_data_node_as_pandas_no_sheet_name.read()
     assert isinstance(data_pandas_no_sheet_name, Dict)
@@ -212,11 +316,15 @@ def test_read_multi_sheet_with_header():
         assert isinstance(data_pandas_no_sheet_name[key], pd.DataFrame)
         assert data_pandas[key].equals(data_pandas_no_sheet_name[key])
 
-    # Create ExcelDataNode with numpy exposed_type
+
+def test_read_multi_sheet_with_header_numpy():
+    data_pandas = pd.read_excel(excel_file_path, sheet_name=sheet_names)
+
+    # With sheet name
     excel_data_node_as_numpy = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "sheet_name": sheet_names, "exposed_type": "numpy"},
+        properties={"path": excel_file_path, "sheet_name": sheet_names, "exposed_type": "numpy"},
     )
 
     data_numpy = excel_data_node_as_numpy.read()
@@ -228,12 +336,13 @@ def test_read_multi_sheet_with_header():
     )
     assert list(data_numpy.keys()) == sheet_names
     for sheet_name in sheet_names:
-        assert np.array_equal(data_pandas[sheet_name], pd.read_excel(path, sheet_name=sheet_name).to_numpy())
+        assert np.array_equal(data_pandas[sheet_name], pd.read_excel(excel_file_path, sheet_name=sheet_name).to_numpy())
 
+    # Without sheet name
     excel_data_node_as_numpy_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "exposed_type": "numpy"},
+        properties={"path": excel_file_path, "exposed_type": "numpy"},
     )
 
     data_numpy_no_sheet_name = excel_data_node_as_numpy_no_sheet_name.read()
@@ -242,23 +351,15 @@ def test_read_multi_sheet_with_header():
         assert isinstance(data_numpy_no_sheet_name[key], np.ndarray)
         assert np.array_equal(data_numpy[key], data_numpy_no_sheet_name[key])
 
-    # Create the same ExcelDataNode but with custom exposed_type
-    non_existing_sheet_name_custom = ExcelDataNode(
-        "bar",
-        Scope.SCENARIO,
-        properties={
-            "path": path,
-            "sheet_name": ["Sheet1", "xyz"],
-            "exposed_type": MyCustomObject1,
-        },
-    )
-    with pytest.raises(NonExistingExcelSheet):
-        non_existing_sheet_name_custom.read()
 
+def test_read_multi_sheet_with_header_single_custom_exposed_type():
+    data_pandas = pd.read_excel(excel_file_path, sheet_name=sheet_names)
+
+    # With sheet name
     excel_data_node_as_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "sheet_name": sheet_names, "exposed_type": MyCustomObject1},
+        properties={"path": excel_file_path, "sheet_name": sheet_names, "exposed_type": MyCustomObject1},
     )
 
     data_custom = excel_data_node_as_custom_object.read()
@@ -275,10 +376,11 @@ def test_read_multi_sheet_with_header():
             assert row_pandas["integer"] == row_custom.integer
             assert row_pandas["text"] == row_custom.text
 
+    # Without sheet name
     excel_data_node_as_custom_object_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "exposed_type": MyCustomObject1},
+        properties={"path": excel_file_path, "exposed_type": MyCustomObject1},
     )
 
     data_custom_no_sheet_name = excel_data_node_as_custom_object_no_sheet_name.read()
@@ -297,31 +399,26 @@ def test_read_multi_sheet_with_header():
             assert row_custom_no_sheet_name.integer == row_custom.integer
             assert row_custom_no_sheet_name.text == row_custom.text
 
-    with pytest.raises(ExposedTypeLengthMismatch):
-        dn = ExcelDataNode(
-            "bar",
-            Scope.SCENARIO,
-            properties={
-                "path": path,
-                "sheet_name": ["Sheet1"],
-                "exposed_type": [MyCustomObject1, MyCustomObject2],
-            },
-        )
-        dn.read()
 
-    custom_class_dict = {"Sheet1": MyCustomObject1, "Sheet2": MyCustomObject2}
+def test_read_multi_sheet_with_header_multiple_custom_exposed_type():
+    data_pandas = pd.read_excel(excel_file_path, sheet_name=sheet_names)
 
+    # With sheet name
     excel_data_node_as_multi_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "sheet_name": sheet_names, "exposed_type": custom_class_dict},
+        properties={"path": excel_file_path, "sheet_name": sheet_names, "exposed_type": custom_class_dict},
     )
     assert excel_data_node_as_multi_custom_object.properties["exposed_type"] == custom_class_dict
 
     excel_data_node_as_multi_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "sheet_name": sheet_names, "exposed_type": [MyCustomObject1, MyCustomObject2]},
+        properties={
+            "path": excel_file_path,
+            "sheet_name": sheet_names,
+            "exposed_type": [MyCustomObject1, MyCustomObject2],
+        },
     )
     assert excel_data_node_as_multi_custom_object.properties["exposed_type"] == [MyCustomObject1, MyCustomObject2]
 
@@ -339,10 +436,11 @@ def test_read_multi_sheet_with_header():
             assert row_pandas["integer"] == row_custom.integer
             assert row_pandas["text"] == row_custom.text
 
+    # Without sheet name
     excel_data_node_as_multi_custom_object_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "exposed_type": custom_class_dict},
+        properties={"path": excel_file_path, "exposed_type": custom_class_dict},
     )
     assert excel_data_node_as_multi_custom_object_no_sheet_name.properties["exposed_type"] == custom_class_dict
 
@@ -363,22 +461,10 @@ def test_read_multi_sheet_with_header():
             assert row_custom_no_sheet_name.text == row_custom.text
 
 
-def test_read_multi_sheet_without_header():
-    not_existing_excel = ExcelDataNode(
-        "foo",
-        Scope.SCENARIO,
-        properties={"path": "WRONG.xlsx", "has_header": False, "sheet_name": ["sheet_name_1", "sheet_name_2"]},
-    )
-    with pytest.raises(NoData):
-        assert not_existing_excel.read() is None
-        not_existing_excel.read_or_raise()
-
-    path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
-    sheet_names = ["Sheet1", "Sheet2"]
-
-    # Create ExcelDataNode without exposed_type (Default is pandas.DataFrame)
+def test_read_multi_sheet_without_header_pandas():
+    # With sheet name
     excel_data_node_as_pandas = ExcelDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "has_header": False, "sheet_name": sheet_names}
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "has_header": False, "sheet_name": sheet_names}
     )
     data_pandas = excel_data_node_as_pandas.read()
     assert isinstance(data_pandas, Dict)
@@ -387,22 +473,29 @@ def test_read_multi_sheet_without_header():
     assert list(data_pandas.keys()) == sheet_names
     for sheet_name in sheet_names:
         assert isinstance(data_pandas[sheet_name], pd.DataFrame)
-        assert data_pandas[sheet_name].equals(pd.read_excel(path, header=None, sheet_name=sheet_name))
+        assert pd.DataFrame.equals(
+            data_pandas[sheet_name], pd.read_excel(excel_file_path, header=None, sheet_name=sheet_name)
+        )
 
+    # Without sheet name
     excel_data_node_as_pandas_no_sheet_name = ExcelDataNode(
-        "bar", Scope.SCENARIO, properties={"path": path, "has_header": False}
+        "bar", Scope.SCENARIO, properties={"path": excel_file_path, "has_header": False}
     )
     data_pandas_no_sheet_name = excel_data_node_as_pandas_no_sheet_name.read()
     assert isinstance(data_pandas_no_sheet_name, Dict)
     for key in data_pandas_no_sheet_name.keys():
         assert isinstance(data_pandas_no_sheet_name[key], pd.DataFrame)
-        assert data_pandas[key].equals(data_pandas_no_sheet_name[key])
+        assert pd.DataFrame.equals(data_pandas[key], data_pandas_no_sheet_name[key])
+
 
-    # Create ExcelDataNode with numpy exposed_type
+def test_read_multi_sheet_without_header_numpy():
+    data_pandas = pd.read_excel(excel_file_path, header=None, sheet_name=sheet_names)
+
+    # With sheet name
     excel_data_node_as_numpy = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "sheet_name": sheet_names, "exposed_type": "numpy"},
+        properties={"path": excel_file_path, "has_header": False, "sheet_name": sheet_names, "exposed_type": "numpy"},
     )
 
     data_numpy = excel_data_node_as_numpy.read()
@@ -415,13 +508,14 @@ def test_read_multi_sheet_without_header():
     assert list(data_numpy.keys()) == sheet_names
     for sheet_name in sheet_names:
         assert np.array_equal(
-            data_pandas[sheet_name], pd.read_excel(path, header=None, sheet_name=sheet_name).to_numpy()
+            data_pandas[sheet_name], pd.read_excel(excel_file_path, header=None, sheet_name=sheet_name).to_numpy()
         )
 
+    # Without sheet name
     excel_data_node_as_numpy_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "exposed_type": "numpy"},
+        properties={"path": excel_file_path, "has_header": False, "exposed_type": "numpy"},
     )
 
     data_numpy_no_sheet_name = excel_data_node_as_numpy_no_sheet_name.read()
@@ -430,25 +524,16 @@ def test_read_multi_sheet_without_header():
         assert isinstance(data_numpy_no_sheet_name[key], np.ndarray)
         assert np.array_equal(data_numpy[key], data_numpy_no_sheet_name[key])
 
-    # Create the same ExcelDataNode but with custom exposed_type
-    non_existing_sheet_name_custom = ExcelDataNode(
-        "bar",
-        Scope.SCENARIO,
-        properties={
-            "path": path,
-            "has_header": False,
-            "sheet_name": ["Sheet1", "xyz"],
-            "exposed_type": MyCustomObject1,
-        },
-    )
-    with pytest.raises(NonExistingExcelSheet):
-        non_existing_sheet_name_custom.read()
 
+def test_read_multi_sheet_without_header_single_custom_exposed_type():
+    data_pandas = pd.read_excel(excel_file_path, header=None, sheet_name=sheet_names)
+
+    # With sheet name
     excel_data_node_as_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
         properties={
-            "path": path,
+            "path": excel_file_path,
             "has_header": False,
             "sheet_name": sheet_names,
             "exposed_type": MyCustomObject1,
@@ -470,10 +555,11 @@ def test_read_multi_sheet_without_header():
             assert row_pandas[1] == row_custom.integer
             assert row_pandas[2] == row_custom.text
 
+    # Without sheet name
     excel_data_node_as_custom_object_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "exposed_type": MyCustomObject1},
+        properties={"path": excel_file_path, "has_header": False, "exposed_type": MyCustomObject1},
     )
 
     data_custom_no_sheet_name = excel_data_node_as_custom_object_no_sheet_name.read()
@@ -492,26 +578,16 @@ def test_read_multi_sheet_without_header():
             assert row_custom_no_sheet_name.integer == row_custom.integer
             assert row_custom_no_sheet_name.text == row_custom.text
 
-    with pytest.raises(ExposedTypeLengthMismatch):
-        dn = ExcelDataNode(
-            "bar",
-            Scope.SCENARIO,
-            properties={
-                "path": path,
-                "sheet_name": ["Sheet1"],
-                "exposed_type": [MyCustomObject1, MyCustomObject2],
-                "has_header": False,
-            },
-        )
-        dn.read()
 
-    custom_class_dict = {"Sheet1": MyCustomObject1, "Sheet2": MyCustomObject2}
+def test_read_multi_sheet_without_header_multiple_custom_exposed_type():
+    data_pandas = pd.read_excel(excel_file_path, header=None, sheet_name=sheet_names)
 
+    # With sheet names
     excel_data_node_as_multi_custom_object = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
         properties={
-            "path": path,
+            "path": excel_file_path,
             "sheet_name": sheet_names,
             "exposed_type": custom_class_dict,
             "has_header": False,
@@ -523,7 +599,7 @@ def test_read_multi_sheet_without_header():
         "bar",
         Scope.SCENARIO,
         properties={
-            "path": path,
+            "path": excel_file_path,
             "sheet_name": sheet_names,
             "exposed_type": [MyCustomObject1, MyCustomObject2],
             "has_header": False,
@@ -545,10 +621,11 @@ def test_read_multi_sheet_without_header():
             assert row_pandas[1] == row_custom.integer
             assert row_pandas[2] == row_custom.text
 
+    # Without sheet names
     excel_data_node_as_multi_custom_object_no_sheet_name = ExcelDataNode(
         "bar",
         Scope.SCENARIO,
-        properties={"path": path, "has_header": False, "exposed_type": custom_class_dict},
+        properties={"path": excel_file_path, "has_header": False, "exposed_type": custom_class_dict},
     )
 
     multi_data_custom_no_sheet_name = excel_data_node_as_multi_custom_object_no_sheet_name.read()