Repository: microsoft/data-factory-testing-framework (language: python)

Issue:
Create Fabric pipelines to use for testing
**Goal:** Create Fabric pipelines to use as examples in the repo

**Success criteria:**
[ ] Create Fabric pipelines with typical activities to be used as examples in the repo


Discussion / hints:

Fix patch:
diff --git a/examples/fabric/nested_ingestion_pipeline/README.md b/examples/fabric/nested_ingestion_pipeline/README.md
new file mode 100644
index 00000000..176bdac0
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/README.md
@@ -0,0 +1,111 @@
+# Nested pipelines
+
+The goal of the pipeline is to iterate through a configuration json file and for each iteration item verify if the combination of the year/month data to be ingested is new or have been updated in the source system. If that is the case then a nested pipeline is invoked and will be responsible to execute the ingestion in 2 steps:
+
+- from the web to an adlsgen2 container
+- from the adlsgen2 container to the Fabric lakehouse
+
+If the data is already in the lakehouse and has not been updated, the ingestion pipeline is not triggered.
+
+![Nested Pipelines](pl_main_pipeline.png)
+
+The main pipeline is composed of 2 top activities:
+
+- a Lookup activity named "Read Configuration File"
+- a ForEach activity named "ForEachYearMonthPair"
+
+Within the ForEach activity, there is a nested If activity called "If New Or Updated" and if this activity evaluates to `True`, the "Invoke Ingestion Pipeline" activity is triggered.
+
+The Ingestion pipeline is composed also by two activities:
+
+- a Copy activity named "Copy NYCData to ADLS" that copies from the Web to an ADLS Gen2 storage,
+- a Copy activity named "Copy NYCData to Lakehouse" that copies from the ADLS Gen2 storage to the Lakehouse.
+
+Additionally the ingestion pipeline expects to receive two parameters when invoked: dynamicMonth and dynamicYear.
+These parameters are dynamically passed from the pair month and year that is running in the current iteration of the ForEach loop.
+
+![Ingestion Pipeline](pl_ingestion_pipeline.png)
+
+## Testing the pl_main activities
+
+The file that contains the individual activity tests for the pl_main_pipeline is located [here](./fabric/tests/test_pl_main_activity.py) and it contains four individual activity tests:
+
+- test_activity_read_configuration_file, which is responsible to test if the json configuration file is correctly located and defined.
+- test_activity_if_true, responsible to check if the boolean result of the condition evaluates to `True`. If so, the pipeline can proceed with the ingestion of new or updated data by triggering the "pl_ingestion" pipeline.
+- test_activity_if_false, responsible to check if the boolean result of the condition evaluates to `False`. If so, the pipeline can proceed to next item in the ForEach Loop.
+- test_activity_invoke_pipeline, responsible to check if the nested pipeline is correctly invoked.
+
+## Testing the pl_ingestion activities
+
+The file that contains the individual activity tests for the pl_ingestion_pipeline is located [here](./fabric/tests/test_pl_ingestion_activity.py) and it contains two individual activity test:
+
+- test_copy_nyc_data_from_web_to_adls2, this test is responsible to test both source (web) and target (adlsgen2) paths expressions to ensure that the paths are well formed and within the correct stablished partition norms.
+- test_copy_nyc_data_from_adls2_to_lakehouse, similar to the previous one but the previous target is now the source (adls2) and the current target is the Fabric Lakehouse.
+
+## Testing the pl_main pipeline with should_evaluate_child_pipelines=True
+
+The framework provides the possibility to test nested pipelines out-of-the-box through the use of the following flag: should_evaluate_child_pipelines.
+
+Setting the should_evaluate_child_pipelines flag to `True`, means that the test is being done on the main pipeline jointly with the child(s) pipeline(s). For more details check the code below:
+below:
+
+```python
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+        should_evaluate_child_pipelines=True,
+    )
+```
+
+The full example is available [here](./fabric/tests/test_pl_main_pipeline_child_flag_true.py).
+
+In this specific case you can test for the number of activities within the pipeline and respective types for example. Furthermore, you can do similar tests to the ones were done on the individual activity tests.
+
+For this particular pipeline, we will have three activities to evaluate, the first one belongs to the pl_main pipeline and the second and third ones belong to the pl_ingestion pipeline:
+
+- read_configuration_file_activity = next(activities)
+- copy_nyc_data_to_adls_pipeline_activity = next(activities)
+- copy_nyc_data_to_lakehouse_pipeline_activity = next(activities)
+
+## Testing the pl_main pipeline with should_evaluate_child_pipelines=False
+
+Setting this flag to `False`,  means that the test is just being done on the main pipeline and the child pipeline will not be loaded for evaluation. For more details check the code below:
+
+```python
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+        should_evaluate_child_pipelines=False,
+    )
+```
+
+The full example is available [here](./fabric/tests/test_pl_main_pipeline_child_flag_false.py).
+
+In this specific case you can test for the number of activities within the pipeline and respective types for example. Furthermore, you can do similar tests to the ones were done on the individual activity tests.
+
+For this particular pipeline, we will have two activities to evaluate:
+
+- read_configuration_file_activity = next(activities)
+- invoke_ingestion_pipeline_activity =  next(activities)
+
+For the control activities, If and ForEach in this specific pipeline, the framework does the evaluation in the background so they won't be available when running the next(activities) function.
+
+## Additional Notes
+
+Be aware that when using the previous json files on your environment, the connection IDs should be adjusted:
+
+```json
+              "externalReferences": {
+                "connection": "a97f9477-e2b0-4a61-bc57-081255466130"
+              }
+```
+
+In this specific example three connections are should be created upfront and referenced in the json definition files:
+
+- the connection to the Web where the Taxi Data is being pulled from
+- connection to the ADLSGen2 storage account
+- connection to the Fabric Lakehouse
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/.platform b/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/.platform
new file mode 100644
index 00000000..e7f6ed95
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/.platform
@@ -0,0 +1,11 @@
+{
+  "$schema": "https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json",
+  "metadata": {
+    "type": "DataPipeline",
+    "displayName": "pl_ingestion"
+  },
+  "config": {
+    "version": "2.0",
+    "logicalId": "f2a51fa4-bd34-4b81-86a5-9c88a446415f"
+  }
+}
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/pipeline-content.json b/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/pipeline-content.json
new file mode 100644
index 00000000..487dd5f4
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/pl_ingestion.DataPipeline/pipeline-content.json
@@ -0,0 +1,187 @@
+{
+  "properties": {
+    "activities": [
+      {
+        "type": "Copy",
+        "typeProperties": {
+          "source": {
+            "type": "BinarySource",
+            "storeSettings": {
+              "type": "HttpReadSettings",
+              "requestMethod": "GET"
+            },
+            "formatSettings": {
+              "type": "BinaryReadSettings"
+            },
+            "datasetSettings": {
+              "type": "Binary",
+              "typeProperties": {
+                "location": {
+                  "type": "HttpServerLocation",
+                  "relativeUrl": {
+                    "value": "@concat('yellow_tripdata_',pipeline().parameters.dynamicyear,'-', pipeline().parameters.dynamicmonth,'.parquet')",
+                    "type": "Expression"
+                  }
+                }
+              },
+              "externalReferences": {
+                "connection": "55f7c027-5f9c-4420-bda7-1cd12412c54a"
+              },
+              "annotations": []
+            }
+          },
+          "sink": {
+            "type": "BinarySink",
+            "storeSettings": {
+              "type": "AzureBlobFSWriteSettings"
+            },
+            "datasetSettings": {
+              "type": "Binary",
+              "typeProperties": {
+                "location": {
+                  "type": "AzureBlobFSLocation",
+                  "fileSystem": "nyctaxidata",
+                  "folderPath": {
+                    "value": "@concat('nyc_taxi_data', '/', pipeline().parameters.dynamicyear,'/',pipeline().parameters.dynamicmonth)",
+                    "type": "Expression"
+                  },
+                  "fileName": {
+                    "value": "@concat('yellow_tripdata_',pipeline().parameters.dynamicyear,'-', pipeline().parameters.dynamicmonth,'.parquet')",
+                    "type": "Expression"
+                  }
+                }
+              },
+              "externalReferences": {
+                "connection": "a97f9477-e2b0-4a61-bc57-081255466130"
+              },
+              "annotations": []
+            }
+          },
+          "enableStaging": false
+        },
+        "policy": {
+          "timeout": "0.12:00:00",
+          "retry": 0,
+          "retryIntervalInSeconds": 30,
+          "secureInput": false,
+          "secureOutput": false
+        },
+        "name": "Copy NYCData from Web to ADLS",
+        "dependsOn": []
+      },
+      {
+        "type": "Copy",
+        "typeProperties": {
+          "source": {
+            "type": "ParquetSource",
+            "storeSettings": {
+              "type": "AzureBlobFSReadSettings",
+              "recursive": true,
+              "enablePartitionDiscovery": false
+            },
+            "formatSettings": {
+              "type": "ParquetReadSettings"
+            },
+            "datasetSettings": {
+              "type": "Parquet",
+              "typeProperties": {
+                "location": {
+                  "type": "AzureBlobFSLocation",
+                  "fileSystem": "nyctaxidata",
+                  "folderPath": {
+                    "value": "@concat(pipeline().parameters.dynamicyear,'/',pipeline().parameters.dynamicmonth)",
+                    "type": "Expression"
+                  },
+                  "fileName": {
+                    "value": "@concat('yellow_tripdata_',pipeline().parameters.dynamicyear,'-', pipeline().parameters.dynamicmonth,'.parquet')",
+                    "type": "Expression"
+                  }
+                },
+                "compressionCodec": "snappy"
+              },
+              "schema": [],
+              "externalReferences": {
+                "connection": "a97f9477-e2b0-4a61-bc57-081255466130"
+              },
+              "annotations": []
+            }
+          },
+          "sink": {
+            "type": "ParquetSink",
+            "storeSettings": {
+              "type": "LakehouseWriteSettings"
+            },
+            "formatSettings": {
+              "type": "ParquetWriteSettings",
+              "enableVertiParquet": true
+            },
+            "datasetSettings": {
+              "type": "Parquet",
+              "typeProperties": {
+                "location": {
+                  "type": "LakehouseLocation",
+                  "folderPath": {
+                    "value": "@concat('nyc_taxi_data', '/',pipeline().parameters.dynamicyear,'/',pipeline().parameters.dynamicmonth)",
+                    "type": "Expression"
+                  },
+                  "fileName": {
+                    "value": "@concat('yellow_tripdata_',pipeline().parameters.dynamicyear,'-', pipeline().parameters.dynamicmonth,'.parquet')",
+                    "type": "Expression"
+                  }
+                },
+                "compressionCodec": "snappy"
+              },
+              "schema": [],
+              "linkedService": {
+                "properties": {
+                  "type": "Lakehouse",
+                  "typeProperties": {
+                    "artifactId": "8aa1ecb3-4662-420b-a02b-845193795d3c",
+                    "workspaceId": "00000000-0000-0000-0000-000000000000",
+                    "rootFolder": "Files"
+                  },
+                  "annotations": []
+                },
+                "name": "ys_lh_testing_framework"
+              },
+              "annotations": []
+            }
+          },
+          "translator": {
+            "type": "TabularTranslator",
+            "typeConversion": true,
+            "typeConversionSettings": {
+              "allowDataTruncation": true,
+              "treatBooleanAsNumber": false
+            }
+          },
+          "enableStaging": false
+        },
+        "policy": {
+          "timeout": "0.12:00:00",
+          "retry": 0,
+          "retryIntervalInSeconds": 30,
+          "secureInput": false,
+          "secureOutput": false
+        },
+        "name": "Copy NYCData from ADLS to Lakehouse",
+        "dependsOn": [
+          {
+            "activity": "Copy NYCData from Web to ADLS",
+            "dependencyConditions": [
+              "Succeeded"
+            ]
+          }
+        ]
+      }
+    ],
+    "parameters": {
+      "dynamicmonth": {
+        "type": "string"
+      },
+      "dynamicyear": {
+        "type": "string"
+      }
+    }
+  }
+}
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/.platform b/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/.platform
new file mode 100644
index 00000000..78906324
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/.platform
@@ -0,0 +1,11 @@
+{
+  "$schema": "https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json",
+  "metadata": {
+    "type": "DataPipeline",
+    "displayName": "pl_main"
+  },
+  "config": {
+    "version": "2.0",
+    "logicalId": "67388440-2c26-46b6-a493-9e4e48c037d9"
+  }
+}
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/pipeline-content.json b/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/pipeline-content.json
new file mode 100644
index 00000000..6f2055c3
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/pl_main.DataPipeline/pipeline-content.json
@@ -0,0 +1,112 @@
+{
+    "properties": {
+      "activities": [
+        {
+          "type": "Lookup",
+          "typeProperties": {
+            "source": {
+              "type": "JsonSource",
+              "storeSettings": {
+                "type": "LakehouseReadSettings",
+                "recursive": true,
+                "enablePartitionDiscovery": false
+              },
+              "formatSettings": {
+                "type": "JsonReadSettings"
+              }
+            },
+            "datasetSettings": {
+              "type": "Json",
+              "typeProperties": {
+                "location": {
+                  "type": "LakehouseLocation",
+                  "folderPath": "config",
+                  "fileName": "lh_config.json"
+                }
+              },
+              "schema": {},
+              "linkedService": {
+                "properties": {
+                  "type": "Lakehouse",
+                  "typeProperties": {
+                    "artifactId": "dd0b83a5-5f0f-4ea9-bb05-368b4c37d15e",
+                    "workspaceId": "00000000-0000-0000-0000-000000000000",
+                    "rootFolder": "Files"
+                  },
+                  "annotations": []
+                },
+                "name": "testing_framework"
+              },
+              "annotations": []
+            }
+          },
+          "policy": {
+            "timeout": "0.12:00:00",
+            "retry": 0,
+            "retryIntervalInSeconds": 30,
+            "secureInput": false,
+            "secureOutput": false
+          },
+          "name": "Read Configuration File",
+          "dependsOn": []
+        },
+        {
+          "type": "ForEach",
+          "typeProperties": {
+            "items": {
+              "value": "@activity('Read Configuration File').output.firstRow.lakeHouseProperties",
+              "type": "Expression"
+            },
+            "activities": [
+              {
+                "type": "IfCondition",
+                "typeProperties": {
+                  "expression": {
+                    "value": "@or(equals(item().lastUpdatedDatalake,''),greaterOrEquals(item().lastUpdatedSourceSystem,addSeconds(item().lastUpdatedDatalake,-1)))",
+                    "type": "Expression"
+                  },
+                  "ifTrueActivities": [
+                    {
+                      "type": "ExecutePipeline",
+                      "typeProperties": {
+                        "pipeline": {
+                          "referenceName": "f2a51fa4-bd34-4b81-86a5-9c88a446415f",
+                          "type": "PipelineReference"
+                        },
+                        "parameters": {
+                          "dynamicmonth": {
+                            "value": "@item().month",
+                            "type": "Expression"
+                          },
+                          "dynamicyear": {
+                            "value": "@item().year",
+                            "type": "Expression"
+                          }
+                        },
+                        "waitOnCompletion": true
+                      },
+                      "name": "Invoke Ingestion Pipeline",
+                      "dependsOn": []
+                    }
+                  ],
+                  "ifFalseActivities": []
+                },
+                "name": "If New Or Updated",
+                "dependsOn": []
+              }
+            ]
+          },
+          "name": "ForEachYearMonthPair",
+          "dependsOn": [
+            {
+              "activity": "Read Configuration File",
+              "dependencyConditions": [
+                "Succeeded"
+              ]
+            }
+          ]
+        }
+      ],
+      "annotations": []
+    }
+  }
diff --git a/examples/fabric/nested_ingestion_pipeline/pl_ingestion_pipeline.png b/examples/fabric/nested_ingestion_pipeline/pl_ingestion_pipeline.png
new file mode 100644
index 00000000..d61b13f9
Binary files /dev/null and b/examples/fabric/nested_ingestion_pipeline/pl_ingestion_pipeline.png differ
diff --git a/examples/fabric/nested_ingestion_pipeline/pl_main_pipeline.png b/examples/fabric/nested_ingestion_pipeline/pl_main_pipeline.png
new file mode 100644
index 00000000..aa58e152
Binary files /dev/null and b/examples/fabric/nested_ingestion_pipeline/pl_main_pipeline.png differ


Test patch:
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_ingestion_activity.py b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_ingestion_activity.py
new file mode 100644
index 00000000..b72c46cd
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_ingestion_activity.py
@@ -0,0 +1,88 @@
+import os
+from pathlib import Path
+
+import pytest
+from data_factory_testing_framework import TestFramework, TestFrameworkType
+from data_factory_testing_framework.models import Pipeline
+from data_factory_testing_framework.state import (
+    PipelineRunState,
+    RunParameter,
+    RunParameterType,
+)
+
+
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+    )
+
+
+@pytest.fixture
+def pipeline(test_framework: TestFramework) -> Pipeline:
+    return test_framework.get_pipeline_by_name("pl_ingestion")
+
+
+def test_copy_nyc_data_from_web_to_adls2(pipeline: Pipeline) -> None:
+    # Arrange
+    activity = pipeline.get_activity_by_name("Copy NYCData from Web to ADLS")
+    state = PipelineRunState(
+        parameters=[
+            RunParameter(RunParameterType.Pipeline, name="dynamicmonth", value="01"),
+            RunParameter(RunParameterType.Pipeline, name="dynamicyear", value="2023"),
+        ],
+    )
+
+    # Act
+    activity.evaluate(state)
+
+    # Assert
+    assert (
+        activity.type_properties["source"]["datasetSettings"]["typeProperties"]["location"]["relativeUrl"].result
+        == "yellow_tripdata_2023-01.parquet"
+    )
+    assert (
+        activity.type_properties["sink"]["datasetSettings"]["typeProperties"]["location"]["fileName"].result
+        == "yellow_tripdata_2023-01.parquet"
+    )
+    assert (
+        activity.type_properties["sink"]["datasetSettings"]["typeProperties"]["location"]["folderPath"].result
+        == "nyc_taxi_data/2023/01"
+    )
+
+
+def test_copy_nyc_data_from_adls2_to_lakehouse(pipeline: Pipeline) -> None:
+    # Arrange
+    activity = pipeline.get_activity_by_name("Copy NYCData from ADLS to Lakehouse")
+    state = PipelineRunState(
+        parameters=[
+            RunParameter(RunParameterType.Pipeline, name="dynamicmonth", value="01"),
+            RunParameter(RunParameterType.Pipeline, name="dynamicyear", value="2023"),
+        ],
+    )
+
+    # Act
+    activity.evaluate(state)
+
+    # Assert
+    assert (
+        activity.type_properties["source"]["datasetSettings"]["typeProperties"]["location"]["fileSystem"]
+        == "nyctaxidata"
+    )
+    assert (
+        activity.type_properties["source"]["datasetSettings"]["typeProperties"]["location"]["folderPath"].result
+        == "2023/01"
+    )
+    assert (
+        activity.type_properties["source"]["datasetSettings"]["typeProperties"]["location"]["fileName"].result
+        == "yellow_tripdata_2023-01.parquet"
+    )
+    assert (
+        activity.type_properties["sink"]["datasetSettings"]["typeProperties"]["location"]["fileName"].result
+        == "yellow_tripdata_2023-01.parquet"
+    )
+    assert (
+        activity.type_properties["sink"]["datasetSettings"]["typeProperties"]["location"]["folderPath"].result
+        == "nyc_taxi_data/2023/01"
+    )
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_activity.py b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_activity.py
new file mode 100644
index 00000000..a2c95337
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_activity.py
@@ -0,0 +1,126 @@
+import os
+from pathlib import Path
+
+import pytest
+from data_factory_testing_framework import TestFramework, TestFrameworkType
+from data_factory_testing_framework.models import Pipeline
+from data_factory_testing_framework.models.activities import IfConditionActivity
+from data_factory_testing_framework.state import (
+    PipelineRunState,
+)
+
+
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+    )
+
+
+@pytest.fixture
+def pipeline(test_framework: TestFramework) -> Pipeline:
+    return test_framework.get_pipeline_by_name("pl_main")
+
+
+def test_activity_read_configuration_file(request: pytest.FixtureRequest, pipeline: Pipeline) -> None:
+    # Arrange
+    activity = pipeline.get_activity_by_name("Read Configuration File")
+
+    # Act
+    state = PipelineRunState()
+    activity.evaluate(state)
+
+    # Assert
+    expected_data_store_type = "Lakehouse"
+    assert (
+        expected_data_store_type == (activity.type_properties["datasetSettings"]["linkedService"]["properties"]["type"])
+    )
+
+    expected_file_name = "lh_config.json"
+    assert expected_file_name == (activity.type_properties["datasetSettings"]["typeProperties"]["location"]["fileName"])
+
+    expected_folder_name = "config"
+    assert (
+        expected_folder_name
+        == (activity.type_properties["datasetSettings"]["typeProperties"]["location"]["folderPath"])
+    )
+
+
+def test_activity_if_true(test_framework: TestFramework, pipeline: Pipeline) -> None:
+    # Arrange
+    foreach_activity = pipeline.get_activity_by_name("ForEachYearMonthPair")
+    child_activity: IfConditionActivity = next(
+        filter(lambda a: a.name == "If New Or Updated", foreach_activity.activities)
+    )
+
+    # Act
+    state = PipelineRunState(
+        iteration_item={
+            "year": "2019",
+            "month": "09",
+            "created": "2023-05-24T11:04:42Z",
+            "lastUpdatedSourceSystem": "2023-05-24T11:04:42Z",
+            "lastUpdatedDatalake": "2023-05-01T11:04:42Z",
+        }
+    )
+
+    child_activity.evaluate(state)
+
+    # Assert
+    assert child_activity.type_properties["expression"].result is True
+
+
+def test_activity_if_false(test_framework: TestFramework, pipeline: Pipeline) -> None:
+    # Arrange
+    foreach_activity = pipeline.get_activity_by_name("ForEachYearMonthPair")
+    child_activity: IfConditionActivity = next(
+        filter(lambda a: a.name == "If New Or Updated", foreach_activity.activities)
+    )
+
+    # Act
+    state = PipelineRunState(
+        iteration_item={
+            "year": "2019",
+            "month": "09",
+            "created": "2023-05-24T11:04:42Z",
+            "lastUpdatedSourceSystem": "2023-05-24T11:04:42Z",
+            "lastUpdatedDatalake": "2023-05-25T11:04:42Z",
+        }
+    )
+
+    child_activity.evaluate(state)
+
+    # Assert
+    assert child_activity.type_properties["expression"].result is False
+
+
+def test_activity_invoke_pipeline(test_framework: TestFramework, pipeline: Pipeline) -> None:
+    # Arrange
+    foreach_activity = pipeline.get_activity_by_name("ForEachYearMonthPair")
+    if_activity: IfConditionActivity = next(
+        filter(lambda a: a.name == "If New Or Updated", foreach_activity.activities)
+    )
+
+    inner_pipeline_activity = next(
+        filter(lambda a: a.name == "Invoke Ingestion Pipeline", if_activity.if_true_activities)
+    )
+
+    # Act
+    state = PipelineRunState(
+        iteration_item={
+            "year": "2019",
+            "month": "09",
+            "created": "2023-05-24T11:04:42Z",
+            "lastUpdatedSourceSystem": "2023-24-01T11:04:42Z",
+            "lastUpdatedDatalake": "2023-05-24T11:04:42Z",
+        }
+    )
+    inner_pipeline_activity.evaluate(state)
+
+    # Assert
+    assert (
+        inner_pipeline_activity.type_properties["pipeline"]["referenceName"] == "f2a51fa4-bd34-4b81-86a5-9c88a446415f"
+    )
+    assert inner_pipeline_activity.type_properties["parameters"]["dynamicmonth"].result == "09"
+    assert inner_pipeline_activity.type_properties["parameters"]["dynamicyear"].result == "2019"
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_false.py b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_false.py
new file mode 100644
index 00000000..007db88a
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_false.py
@@ -0,0 +1,100 @@
+import os
+from pathlib import Path
+
+import pytest
+from data_factory_testing_framework import TestFramework, TestFrameworkType
+from data_factory_testing_framework.models import Pipeline
+from data_factory_testing_framework.state import DependencyCondition, RunParameter, RunParameterType
+
+
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+        should_evaluate_child_pipelines=False,
+    )
+
+
+@pytest.fixture
+def pipeline(test_framework: TestFramework) -> Pipeline:
+    return test_framework.get_pipeline_by_name("pl_main")
+
+
+def test_pl_main_pipeline(test_framework: TestFramework) -> None:
+    # Arrange
+    pipeline = test_framework.get_pipeline_by_name("pl_main")
+
+    # Act
+    activities = test_framework.evaluate_pipeline(
+        pipeline,
+        parameters=[
+            RunParameter(RunParameterType.Pipeline, name="dynamicmonth", value="09"),
+            RunParameter(RunParameterType.Pipeline, name="dynamicyear", value="2019"),
+        ],
+    )
+
+    # Assert pipeline general information
+    assert pipeline.name == "pl_main"
+    assert len(pipeline.activities) == 2
+
+    # Assert pipeline activities information
+    # Activity: Read Configuration File
+    read_configuration_file_activity = next(activities)
+    assert read_configuration_file_activity.name == "Read Configuration File"
+    assert read_configuration_file_activity.type == "Lookup"
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["linkedService"]["properties"]["type"]
+        == "Lakehouse"
+    )
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["typeProperties"]["location"]["fileName"]
+        == "lh_config.json"
+    )
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["typeProperties"]["location"]["folderPath"]
+        == "config"
+    )
+
+    # In order to simulate how the activity would look like when the ForEach activity iterates through the first row
+    # the result of the activity needs to be set upfront.
+    # Because of the following expression: "@activity('Read Configuration File').output.firstRow.lakeHouseProperties",
+    # we need to add the "lakeHouseProperties" and "firstRow" key to the result because the Framework is unaware of these.
+    # For the activity test (test_pl_main_activity.py) we just use the inner properties .{"year:"2019","month":"09", ...}.
+    # If you are unaware of how the activity result should look like, you can run the pipeline and check the output of the activity.
+    read_configuration_file_activity.set_result(
+        DependencyCondition.SUCCEEDED,
+        {
+            "firstRow": {
+                "lakeHouseProperties": [
+                    {
+                        "year": "2019",
+                        "month": "09",
+                        "created": "2023-05-24T11:04:42Z",
+                        "lastUpdatedSourceSystem": "2023-05-24T11:04:42Z",
+                        "lastUpdatedDatalake": "2023-05-01T11:04:42Z",
+                    }
+                ]
+            }
+        },
+    )
+
+    # Next activity is: Invoke Ingestion Pipeline - because for the previous control activities:
+    # If and ForEach the framework does the evaluation in the background and
+    # it doesn't need to be done manually.
+    # With that, the next activity to be evaluated is the Invoke Ingestion Pipeline.
+
+    # Use the following code if your should_evaluate_child_pipelines is set to False
+    invoke_ingestion_pipeline_activity = next(activities)
+    assert invoke_ingestion_pipeline_activity.name == "Invoke Ingestion Pipeline"
+    assert invoke_ingestion_pipeline_activity.type == "ExecutePipeline"
+    assert (
+        invoke_ingestion_pipeline_activity.type_properties["pipeline"]["referenceName"]
+        == "f2a51fa4-bd34-4b81-86a5-9c88a446415f"
+    )
+    assert invoke_ingestion_pipeline_activity.type_properties["parameters"]["dynamicmonth"].result == "09"
+    assert invoke_ingestion_pipeline_activity.type_properties["parameters"]["dynamicyear"].result == "2019"
+
+    # Assert that there are no more activities
+    with pytest.raises(StopIteration):
+        next(activities)
diff --git a/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_true.py b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_true.py
new file mode 100644
index 00000000..a96849f7
--- /dev/null
+++ b/examples/fabric/nested_ingestion_pipeline/fabric/tests/test_pl_main_pipeline_child_flag_true.py
@@ -0,0 +1,146 @@
+import os
+from pathlib import Path
+
+import pytest
+from data_factory_testing_framework import TestFramework, TestFrameworkType
+from data_factory_testing_framework.models import Pipeline
+from data_factory_testing_framework.state import DependencyCondition, RunParameter, RunParameterType
+
+
+@pytest.fixture
+def test_framework(request: pytest.FixtureRequest) -> TestFramework:
+    return TestFramework(
+        framework_type=TestFrameworkType.Fabric,
+        root_folder_path=os.path.join(Path(request.fspath.dirname).parent),
+        should_evaluate_child_pipelines=True,
+    )
+
+
+@pytest.fixture
+def pipeline(test_framework: TestFramework) -> Pipeline:
+    return test_framework.get_pipeline_by_name("pl_main")
+
+
+def test_pl_main_pipeline(test_framework: TestFramework) -> None:
+    # Arrange
+    pipeline = test_framework.get_pipeline_by_name("pl_main")
+
+    # Act
+    activities = test_framework.evaluate_pipeline(
+        pipeline,
+        parameters=[
+            RunParameter(RunParameterType.Pipeline, name="dynamicmonth", value="09"),
+            RunParameter(RunParameterType.Pipeline, name="dynamicyear", value="2019"),
+        ],
+    )
+
+    # Assert pipeline general information
+    assert pipeline.name == "pl_main"
+    assert len(pipeline.activities) == 2
+
+    # Assert pipeline activities information
+    # Activity: Read Configuration File
+    read_configuration_file_activity = next(activities)
+    assert read_configuration_file_activity.name == "Read Configuration File"
+    assert read_configuration_file_activity.type == "Lookup"
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["linkedService"]["properties"]["type"]
+        == "Lakehouse"
+    )
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["typeProperties"]["location"]["fileName"]
+        == "lh_config.json"
+    )
+    assert (
+        read_configuration_file_activity.type_properties["datasetSettings"]["typeProperties"]["location"]["folderPath"]
+        == "config"
+    )
+
+    # In order to simulate how the activity would look like when the ForEach activity iterates through the first row
+    # the result of the activity needs to be set upfront.
+    # Because of the following expression: "@activity('Read Configuration File').output.firstRow.lakeHouseProperties",
+    # we need to add the "lakeHouseProperties" and "firstRow" key to the result because the Framework is unaware of these.
+    # For the activity test (test_pl_main_activity.py) we just use the inner properties .{"year:"2019","month":"09", ...}.
+    # If you are unaware of how the activity result should look like, you can run the pipeline and check the output of the activity.
+    read_configuration_file_activity.set_result(
+        DependencyCondition.SUCCEEDED,
+        {
+            "firstRow": {
+                "lakeHouseProperties": [
+                    {
+                        "year": "2019",
+                        "month": "09",
+                        "created": "2023-05-24T11:04:42Z",
+                        "lastUpdatedSourceSystem": "2023-05-24T11:04:42Z",
+                        "lastUpdatedDatalake": "2023-05-01T11:04:42Z",
+                    }
+                ]
+            }
+        },
+    )
+
+    # Next activity is the first activity of the child pipeline: "Copy NYCData from Web to ADLS" as we have the evaluate child flag set to true.
+    # Because for the previous control activities: If and ForEach the framework does the evaluation in the background and
+    # it doesn't need to be done manually in the test.
+    # With that, the next activity to be evaluated is the "Copy NYCData from Web to ADLS".
+
+    # Use the following code if your should_evaluate_child_pipelines is set to True
+    copy_nyc_data_to_adls_pipeline_activity = next(activities)
+    assert copy_nyc_data_to_adls_pipeline_activity.name == "Copy NYCData from Web to ADLS"
+    assert copy_nyc_data_to_adls_pipeline_activity.type == "Copy"
+    assert (
+        copy_nyc_data_to_adls_pipeline_activity.type_properties["source"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["relativeUrl"].result
+        == "yellow_tripdata_2019-09.parquet"
+    )
+    assert (
+        copy_nyc_data_to_adls_pipeline_activity.type_properties["sink"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["fileName"].result
+        == "yellow_tripdata_2019-09.parquet"
+    )
+    assert (
+        copy_nyc_data_to_adls_pipeline_activity.type_properties["sink"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["folderPath"].result
+        == "nyc_taxi_data/2019/09"
+    )
+
+    # Next activity is the second activity of the child pipeline: "Copy NYCData from ADLS to Lakehouse"
+    copy_nyc_data_to_lakehouse_pipeline_activity = next(activities)
+    assert copy_nyc_data_to_lakehouse_pipeline_activity.name == "Copy NYCData from ADLS to Lakehouse"
+    assert copy_nyc_data_to_lakehouse_pipeline_activity.type == "Copy"
+    assert (
+        copy_nyc_data_to_lakehouse_pipeline_activity.type_properties["source"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["fileSystem"]
+        == "nyctaxidata"
+    )
+    assert (
+        copy_nyc_data_to_lakehouse_pipeline_activity.type_properties["source"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["folderPath"].result
+        == "2019/09"
+    )
+    assert (
+        copy_nyc_data_to_lakehouse_pipeline_activity.type_properties["source"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["fileName"].result
+        == "yellow_tripdata_2019-09.parquet"
+    )
+    assert (
+        copy_nyc_data_to_lakehouse_pipeline_activity.type_properties["sink"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["fileName"].result
+        == "yellow_tripdata_2019-09.parquet"
+    )
+    assert (
+        copy_nyc_data_to_lakehouse_pipeline_activity.type_properties["sink"]["datasetSettings"]["typeProperties"][
+            "location"
+        ]["folderPath"].result
+        == "nyc_taxi_data/2019/09"
+    )
+    # Assert that there are no more activities
+    with pytest.raises(StopIteration):
+        next(activities)