Skip to content

Commit 92b120e

Browse files
feat: Add initial test suite for advanced_operations examples
I've created the test infrastructure, including a `tests` directory, `__init__.py`, and a `conftest.py` with a `mock_google_ads_client` fixture. I've also generated placeholder test files for all 18 scripts in `examples/advanced_operations`. I've implemented basic tests for 13 of these scripts. These tests verify that the main() function of each script can execute without unhandled exceptions when provided with a mocked GoogleAdsClient and appropriate mock arguments. This includes mocking service calls, enums, and patching external utilities like uuid and requests.get. Scripts with basic tests: - add_ad_customizer.py - add_ad_group_bid_modifier.py - add_app_campaign.py - add_bidding_data_exclusion.py - add_bidding_seasonality_adjustment.py - add_call_ad.py - add_demand_gen_campaign.py - add_display_upload_ad.py - add_dynamic_page_feed_asset.py - add_dynamic_search_ads.py - add_performance_max_campaign.py - add_responsive_search_ad_full.py - add_smart_campaign.py Further work is needed to implement tests for the remaining 5 scripts and potentially enhance the existing tests with more specific assertions.
1 parent 83bf661 commit 92b120e

20 files changed

Lines changed: 1238 additions & 0 deletions

examples/advanced_operations/tests/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pytest
2+
from unittest.mock import MagicMock, PropertyMock
3+
4+
@pytest.fixture
5+
def mock_google_ads_client():
6+
"""Fixture to mock the GoogleAdsClient."""
7+
mock_client = MagicMock()
8+
9+
# Mock the get_service method
10+
# This will return a new MagicMock for any service name requested
11+
mock_client.get_service = MagicMock(return_value=MagicMock())
12+
13+
# Mock the get_type method
14+
# This will return a new MagicMock for any type name requested
15+
# We can refine this later if specific type behaviors are needed
16+
mock_client.get_type = MagicMock(return_value=MagicMock())
17+
18+
# Mock the enums attribute
19+
# This allows access like client.enums.SomeEnum.VALUE
20+
# For now, we'll make it a MagicMock. If specific enums are needed,
21+
# they can be added as properties to this mock_enums object.
22+
mock_enums = MagicMock()
23+
24+
# Example of mocking a specific enum if needed for a test:
25+
# mock_campaign_status_enum = MagicMock()
26+
# mock_campaign_status_enum.PAUSED = "PAUSED_ENUM_VALUE" # Or an int if that's what the API uses
27+
# type(mock_enums).CampaignStatusEnum = PropertyMock(return_value=mock_campaign_status_enum)
28+
29+
mock_client.enums = mock_enums
30+
31+
return mock_client
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import pytest
2+
from unittest.mock import MagicMock
3+
4+
# Assuming 'examples' is in the Python path or using relative imports
5+
# If 'examples' is not directly in PYTHONPATH, adjust the import accordingly.
6+
# For example, if the tests are run from the directory containing 'examples':
7+
from examples.advanced_operations.add_ad_customizer import main
8+
9+
# If the above import doesn't work due to path issues, the test runner might
10+
# need to be configured, or sys.path might need adjustment in a conftest.py
11+
# or at the beginning of the test file. For now, we assume it works.
12+
13+
def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
14+
"""Tests that the main function runs without raising an exception."""
15+
mock_customer_id = "1234567890"
16+
mock_ad_group_id = "0987654321"
17+
18+
# Configure mock service calls if they need to return specific structures
19+
# For example, if a service call returns an object with a 'resource_name'
20+
mock_customizer_attribute_service = mock_google_ads_client.get_service("CustomizerAttributeService")
21+
mock_mutate_response = MagicMock()
22+
mock_result = MagicMock()
23+
mock_result.resource_name = "mock_resource_name_customizer_attr"
24+
mock_mutate_response.results = [mock_result]
25+
mock_customizer_attribute_service.mutate_customizer_attributes.return_value = mock_mutate_response
26+
27+
mock_ad_group_customizer_service = mock_google_ads_client.get_service("AdGroupCustomizerService")
28+
mock_mutate_ad_group_customizers_response = MagicMock()
29+
mock_ad_group_customizer_result = MagicMock()
30+
mock_ad_group_customizer_result.resource_name = "mock_resource_name_ad_group_customizer"
31+
mock_mutate_ad_group_customizers_response.results = [mock_ad_group_customizer_result]
32+
mock_ad_group_customizer_service.mutate_ad_group_customizers.return_value = mock_mutate_ad_group_customizers_response
33+
34+
mock_googleads_service = mock_google_ads_client.get_service("GoogleAdsService")
35+
mock_googleads_service.ad_group_path.return_value = "mock/ad_group/path"
36+
37+
mock_ad_group_ad_service = mock_google_ads_client.get_service("AdGroupAdService")
38+
mock_mutate_ad_group_ads_response = MagicMock()
39+
mock_ad_group_ad_result = MagicMock()
40+
mock_ad_group_ad_result.resource_name = "mock_resource_name_ad_group_ad"
41+
mock_mutate_ad_group_ads_response.results = [mock_ad_group_ad_result]
42+
mock_ad_group_ad_service.mutate_ad_group_ads.return_value = mock_mutate_ad_group_ads_response
43+
44+
# Mock enums used by the script
45+
mock_enums = mock_google_ads_client.enums
46+
mock_enums.CustomizerAttributeTypeEnum.TEXT = "TEXT"
47+
mock_enums.CustomizerAttributeTypeEnum.PRICE = "PRICE"
48+
mock_enums.ServedAssetFieldTypeEnum.HEADLINE_1 = "HEADLINE_1"
49+
50+
51+
try:
52+
main(
53+
mock_google_ads_client,
54+
mock_customer_id,
55+
mock_ad_group_id,
56+
)
57+
except Exception as e:
58+
pytest.fail(f"main function raised an exception: {e}")
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
from unittest.mock import MagicMock
3+
4+
from examples.advanced_operations.add_ad_group_bid_modifier import main
5+
6+
def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
7+
"""Tests that the main function runs without raising an exception."""
8+
mock_customer_id = "1234567890"
9+
mock_ad_group_id = "0987654321"
10+
mock_bid_modifier_value = 1.5
11+
12+
# Mock services and their responses
13+
mock_ad_group_service = mock_google_ads_client.get_service("AdGroupService")
14+
mock_ad_group_service.ad_group_path.return_value = "customers/1234567890/adGroups/0987654321"
15+
16+
mock_ad_group_bm_service = mock_google_ads_client.get_service("AdGroupBidModifierService")
17+
mock_mutate_response = MagicMock()
18+
mock_result = MagicMock()
19+
mock_result.resource_name = "mock_resource_name_ad_group_bid_modifier"
20+
mock_mutate_response.results = [mock_result]
21+
mock_ad_group_bm_service.mutate_ad_group_bid_modifiers.return_value = mock_mutate_response
22+
23+
# Mock enums
24+
mock_device_enum = MagicMock()
25+
mock_device_enum.MOBILE = "MOBILE_DEVICE" # Or actual enum value if known/needed
26+
mock_google_ads_client.enums.DeviceEnum = mock_device_enum
27+
28+
# Mock types
29+
# The get_type method in conftest returns a MagicMock by default.
30+
# We can configure its "create" attribute if necessary, but often not needed
31+
# if the "create" object is just passed to a service method.
32+
# mock_ad_group_bid_modifier_operation = mock_google_ads_client.get_type("AdGroupBidModifierOperation")
33+
# If specific attributes on ad_group_bid_modifier (the .create object) were accessed directly
34+
# in the script before being passed to the service, we'd mock them here.
35+
# e.g. mock_ad_group_bid_modifier_operation.create.some_attribute = "some_value"
36+
37+
try:
38+
main(
39+
mock_google_ads_client,
40+
mock_customer_id,
41+
mock_ad_group_id,
42+
mock_bid_modifier_value,
43+
)
44+
except Exception as e:
45+
pytest.fail(f"main function raised an exception: {e}")
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import pytest
2+
from unittest.mock import MagicMock, patch
3+
4+
from examples.advanced_operations.add_app_campaign import main
5+
6+
@patch("examples.advanced_operations.add_app_campaign.uuid4", return_value=MagicMock(hex="testuuid"))
7+
def test_main_runs_successfully(mock_uuid4: MagicMock, mock_google_ads_client: MagicMock) -> None:
8+
"""Tests that the main function runs without raising an exception."""
9+
mock_customer_id = "1234567890"
10+
11+
# --- Mock service responses ---
12+
# CampaignBudgetService
13+
mock_budget_service = mock_google_ads_client.get_service("CampaignBudgetService")
14+
mock_budget_response = MagicMock()
15+
mock_budget_result = MagicMock()
16+
mock_budget_result.resource_name = "customers/1234567890/campaignBudgets/budget_testuuid"
17+
mock_budget_response.results = [mock_budget_result]
18+
mock_budget_service.mutate_campaign_budgets.return_value = mock_budget_response
19+
20+
# CampaignService
21+
mock_campaign_service = mock_google_ads_client.get_service("CampaignService")
22+
mock_campaign_response = MagicMock()
23+
mock_campaign_result = MagicMock()
24+
mock_campaign_result.resource_name = "customers/1234567890/campaigns/campaign_testuuid"
25+
mock_campaign_response.results = [mock_campaign_result]
26+
mock_campaign_service.mutate_campaigns.return_value = mock_campaign_response
27+
28+
# GeoTargetConstantService
29+
mock_geo_service = mock_google_ads_client.get_service("GeoTargetConstantService")
30+
mock_geo_service.geo_target_constant_path.side_effect = lambda x: f"geoTargetConstants/{x}"
31+
32+
# GoogleAdsService (for language_constant_path)
33+
mock_googleads_service = mock_google_ads_client.get_service("GoogleAdsService")
34+
mock_googleads_service.language_constant_path.side_effect = lambda x: f"languageConstants/{x}"
35+
36+
# CampaignCriterionService
37+
mock_campaign_criterion_service = mock_google_ads_client.get_service("CampaignCriterionService")
38+
mock_criterion_response = MagicMock()
39+
# Simulate multiple results for campaign criteria
40+
mock_criterion_result_loc1 = MagicMock()
41+
mock_criterion_result_loc1.resource_name = "criterion_loc1"
42+
mock_criterion_result_loc2 = MagicMock()
43+
mock_criterion_result_loc2.resource_name = "criterion_loc2"
44+
mock_criterion_result_lang1 = MagicMock()
45+
mock_criterion_result_lang1.resource_name = "criterion_lang1"
46+
mock_criterion_result_lang2 = MagicMock()
47+
mock_criterion_result_lang2.resource_name = "criterion_lang2"
48+
mock_criterion_response.results = [
49+
mock_criterion_result_loc1,
50+
mock_criterion_result_loc2,
51+
mock_criterion_result_lang1,
52+
mock_criterion_result_lang2
53+
]
54+
mock_campaign_criterion_service.mutate_campaign_criteria.return_value = mock_criterion_response
55+
56+
# AdGroupService
57+
mock_ad_group_service = mock_google_ads_client.get_service("AdGroupService")
58+
mock_ad_group_response = MagicMock()
59+
mock_ad_group_result = MagicMock()
60+
mock_ad_group_result.resource_name = "customers/1234567890/adGroups/adgroup_testuuid"
61+
mock_ad_group_response.results = [mock_ad_group_result]
62+
mock_ad_group_service.mutate_ad_groups.return_value = mock_ad_group_response
63+
64+
# AdGroupAdService
65+
mock_ad_group_ad_service = mock_google_ads_client.get_service("AdGroupAdService")
66+
mock_ad_group_ad_response = MagicMock()
67+
mock_ad_group_ad_result = MagicMock()
68+
mock_ad_group_ad_result.resource_name = "customers/1234567890/adGroupAds/ad_testuuid"
69+
mock_ad_group_ad_response.results = [mock_ad_group_ad_result]
70+
mock_ad_group_ad_service.mutate_ad_group_ads.return_value = mock_ad_group_ad_response
71+
72+
# --- Mock enums used by the script ---
73+
mock_enums = mock_google_ads_client.enums
74+
mock_enums.BudgetDeliveryMethodEnum.STANDARD = "STANDARD"
75+
mock_enums.CampaignStatusEnum.PAUSED = "PAUSED"
76+
mock_enums.AdvertisingChannelTypeEnum.MULTI_CHANNEL = "MULTI_CHANNEL"
77+
mock_enums.AdvertisingChannelSubTypeEnum.APP_CAMPAIGN = "APP_CAMPAIGN"
78+
mock_enums.AppCampaignAppStoreEnum.GOOGLE_APP_STORE = "GOOGLE_APP_STORE"
79+
mock_enums.AppCampaignBiddingStrategyGoalTypeEnum.OPTIMIZE_INSTALLS_TARGET_INSTALL_COST = "OPTIMIZE_INSTALLS_TARGET_INSTALL_COST"
80+
mock_enums.AdGroupStatusEnum.ENABLED = "ENABLED"
81+
mock_enums.AdGroupAdStatusEnum.ENABLED = "ENABLED"
82+
83+
# --- Mock types ---
84+
# Most types are simple containers, MagicMock default behavior is often sufficient.
85+
# For AdTextAsset, it's created and then its `text` attribute is set.
86+
# The default MagicMock returned by get_type will handle this.
87+
# mock_ad_text_asset = mock_google_ads_client.get_type("AdTextAsset")
88+
# If create_ad_text_asset returned something complex that was then used,
89+
# we would mock that type more specifically.
90+
91+
try:
92+
main(
93+
mock_google_ads_client,
94+
mock_customer_id,
95+
)
96+
except Exception as e:
97+
pytest.fail(f"main function raised an exception: {e}")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pytest
2+
from unittest.mock import MagicMock, patch
3+
4+
from examples.advanced_operations.add_bidding_data_exclusion import main
5+
6+
@patch("examples.advanced_operations.add_bidding_data_exclusion.uuid4", return_value=MagicMock(hex="testuuid"))
7+
def test_main_runs_successfully(mock_uuid4: MagicMock, mock_google_ads_client: MagicMock) -> None:
8+
"""Tests that the main function runs without raising an exception."""
9+
mock_customer_id = "123"
10+
mock_start_date_time = "2023-10-01 00:00:00"
11+
mock_end_date_time = "2023-10-02 00:00:00"
12+
13+
# Mock BiddingDataExclusionService
14+
mock_bde_service = mock_google_ads_client.get_service("BiddingDataExclusionService")
15+
mock_mutate_response = MagicMock()
16+
mock_result = MagicMock()
17+
mock_result.resource_name = "mock_resource_name_bde"
18+
mock_mutate_response.results = [mock_result]
19+
mock_bde_service.mutate_bidding_data_exclusions.return_value = mock_mutate_response
20+
21+
# Mock enums
22+
mock_enums = mock_google_ads_client.enums
23+
mock_enums.SeasonalityEventScopeEnum.CHANNEL = "CHANNEL"
24+
mock_enums.AdvertisingChannelTypeEnum.SEARCH = "SEARCH"
25+
# The script also uses DeviceEnum, but it's commented out, so not strictly needed for this test pass
26+
# mock_enums.DeviceEnum.MOBILE = "MOBILE"
27+
28+
try:
29+
main(
30+
mock_google_ads_client,
31+
mock_customer_id,
32+
mock_start_date_time,
33+
mock_end_date_time,
34+
)
35+
except Exception as e:
36+
pytest.fail(f"main function raised an exception: {e}")
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
from unittest.mock import MagicMock, patch
3+
4+
from examples.advanced_operations.add_bidding_seasonality_adjustment import main
5+
6+
@patch("examples.advanced_operations.add_bidding_seasonality_adjustment.uuid4", return_value=MagicMock(hex="testuuid"))
7+
def test_main_runs_successfully(mock_uuid4: MagicMock, mock_google_ads_client: MagicMock) -> None:
8+
"""Tests that the main function runs without raising an exception."""
9+
mock_customer_id = "123"
10+
mock_start_date_time = "2023-10-01 00:00:00"
11+
mock_end_date_time = "2023-10-02 00:00:00"
12+
mock_bid_modifier = 1.5
13+
14+
# Mock BiddingSeasonalityAdjustmentService
15+
mock_bsa_service = mock_google_ads_client.get_service("BiddingSeasonalityAdjustmentService")
16+
mock_mutate_response = MagicMock()
17+
mock_result = MagicMock()
18+
mock_result.resource_name = "mock_resource_name_bsa"
19+
mock_mutate_response.results = [mock_result]
20+
mock_bsa_service.mutate_bidding_seasonality_adjustments.return_value = mock_mutate_response
21+
22+
# Mock enums
23+
mock_enums = mock_google_ads_client.enums
24+
mock_enums.SeasonalityEventScopeEnum.CHANNEL = "CHANNEL"
25+
mock_enums.AdvertisingChannelTypeEnum.SEARCH = "SEARCH"
26+
# The script also uses DeviceEnum, but it's commented out, so not strictly needed for this test pass
27+
# mock_enums.DeviceEnum.MOBILE = "MOBILE"
28+
29+
try:
30+
main(
31+
mock_google_ads_client,
32+
mock_customer_id,
33+
mock_start_date_time,
34+
mock_end_date_time,
35+
mock_bid_modifier,
36+
)
37+
except Exception as e:
38+
pytest.fail(f"main function raised an exception: {e}")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pytest
2+
from unittest.mock import MagicMock
3+
4+
from examples.advanced_operations.add_call_ad import main
5+
6+
def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
7+
"""Tests that the main function runs without raising an exception."""
8+
mock_customer_id = "123"
9+
mock_ad_group_id = "456"
10+
mock_phone_number = "(800) 555-0100"
11+
mock_phone_country = "US"
12+
mock_conversion_action_id = None # Or a string "789" if testing that path
13+
14+
# Mock GoogleAdsService for path helpers
15+
mock_googleads_service = mock_google_ads_client.get_service("GoogleAdsService")
16+
mock_googleads_service.ad_group_path.return_value = f"customers/{mock_customer_id}/adGroups/{mock_ad_group_id}"
17+
if mock_conversion_action_id:
18+
mock_googleads_service.conversion_action_path.return_value = f"customers/{mock_customer_id}/conversionActions/{mock_conversion_action_id}"
19+
20+
# Mock AdGroupAdService
21+
mock_ad_group_ad_service = mock_google_ads_client.get_service("AdGroupAdService")
22+
mock_mutate_response = MagicMock()
23+
mock_result = MagicMock()
24+
mock_result.resource_name = "mock_resource_name_ad_group_ad"
25+
mock_mutate_response.results = [mock_result]
26+
mock_ad_group_ad_service.mutate_ad_group_ads.return_value = mock_mutate_response
27+
28+
# Mock enums
29+
mock_enums = mock_google_ads_client.enums
30+
mock_enums.AdGroupAdStatusEnum.PAUSED = "PAUSED"
31+
mock_enums.CallConversionReportingStateEnum.USE_RESOURCE_LEVEL_CALL_CONVERSION_ACTION = "USE_RESOURCE_LEVEL_CALL_CONVERSION_ACTION"
32+
# If mock_conversion_action_id is None, this enum below is used.
33+
mock_enums.CallConversionReportingStateEnum.USE_ACCOUNT_LEVEL_CALL_CONVERSION_ACTION = "USE_ACCOUNT_LEVEL_CALL_CONVERSION_ACTION"
34+
35+
36+
try:
37+
main(
38+
mock_google_ads_client,
39+
mock_customer_id,
40+
mock_ad_group_id,
41+
mock_phone_number,
42+
mock_phone_country,
43+
mock_conversion_action_id,
44+
)
45+
except Exception as e:
46+
pytest.fail(f"main function raised an exception: {e}")

0 commit comments

Comments
 (0)