55import os
66import unittest
77from logging import getLogger
8+ from unittest .mock import patch
89
910from dotenv import load_dotenv
1011
@@ -24,4 +25,48 @@ def setUpClass(cls):
2425 """Set up class-level resources."""
2526 if not cls .is_testable :
2627 logger .warning ("CI - skipping tests that require Azure ML workspace connection" )
28+ cls ._setup_azure_mocks ()
2729 return
30+
31+ @classmethod
32+ def tearDownClass (cls ):
33+ """Clean up class-level resources."""
34+ if not cls .is_testable :
35+ # Stop all patches
36+ patchers = [
37+ "ml_client_patcher" ,
38+ "credential_patcher" ,
39+ "dataset_v2_patcher" ,
40+ "workspace_patcher" ,
41+ "dataset_v1_patcher" ,
42+ "compute_patcher" ,
43+ "experiment_patcher" ,
44+ ]
45+ for patcher_name in patchers :
46+ if hasattr (cls , patcher_name ):
47+ getattr (cls , patcher_name ).stop ()
48+
49+ @classmethod
50+ def _setup_azure_mocks (cls ):
51+ """Set up mocks for Azure ML clients when not testable."""
52+ logger .info ("Setting up Azure ML mocks for non-testable CI-CD environment" )
53+
54+ # Patch at the source before any imports happen
55+ cls .ml_client_patcher = patch ("azure.ai.ml.MLClient" )
56+ cls .mock_ml_client = cls .ml_client_patcher .start ()
57+
58+ cls .credential_patcher = patch ("azure.identity.DefaultAzureCredential" )
59+ cls .mock_credential = cls .credential_patcher .start ()
60+
61+ # Also patch other Azure classes at their source
62+ cls .workspace_patcher = patch ("azureml.core.Workspace" )
63+ cls .mock_workspace = cls .workspace_patcher .start ()
64+
65+ cls .dataset_v1_patcher = patch ("azureml.core.Dataset" )
66+ cls .mock_dataset_v1 = cls .dataset_v1_patcher .start ()
67+
68+ cls .compute_patcher = patch ("azureml.core.compute.AmlCompute" )
69+ cls .mock_compute = cls .compute_patcher .start ()
70+
71+ cls .experiment_patcher = patch ("azureml.core.Experiment" )
72+ cls .mock_experiment = cls .experiment_patcher .start ()
0 commit comments