Skip to content

Commit 2649f13

Browse files
fix(tests): Use custom class for rows in test_get_ad_group_bid_modifiers.py
Replaced `MagicMock` with a custom `MockGoogleAdsRow` class for simulating `GoogleAdsRow` objects in the test setup of `test_get_ad_group_bid_modifiers.py`. This change is intended to ensure that the `ad_group_bid_modifier` attribute of a row, when assigned an instance of the custom `MockAdGroupBidModifierModel` class, retains its specific type when accessed by the script being tested. This should resolve the persistent `AttributeError: type object 'MagicMock' has no attribute 'pb'`, by ensuring `type(modifier)` in the script correctly evaluates to `MockAdGroupBidModifierModel`.
1 parent ac63ed7 commit 2649f13

File tree

1 file changed

+23
-39
lines changed

1 file changed

+23
-39
lines changed
Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
import pytest
22
from unittest.mock import MagicMock
3-
import pytest # Ensure pytest is imported if not already
3+
# pytest is imported twice in the current file, remove one
4+
# import pytest # Ensure pytest is imported if not already
45

56
from examples.advanced_operations.get_ad_group_bid_modifiers import main
67

78
class MockAdGroupBidModifierModel:
8-
def __init__(self, client_enums_mock): # Renamed to avoid conflict with client fixture
9-
# --- Begin: Attributes accessed directly by the script's print statement or logic ---
9+
def __init__(self, client_enums_mock):
1010
self.criterion_id = MagicMock(name="criterion_id_attr")
1111
self.bid_modifier = MagicMock(name="bid_modifier_attr")
1212

1313
self.device = MagicMock(name="device_attr")
14-
# Ensure type_ itself is a mock that has a .name attribute
1514
self.device.type_ = MagicMock(name="device_type_attr")
16-
self.device.type_.name = "DEFAULT_DEVICE_NAME" # Default .name
15+
self.device.type_.name = "DEFAULT_DEVICE_NAME"
1716

18-
# Hotel criteria attributes (initialize them as MagicMocks)
1917
self.hotel_date_selection_type = MagicMock(name="hotel_date_selection_type_attr")
2018
self.hotel_date_selection_type.type_ = MagicMock(name="hotel_date_selection_type_type_attr")
2119
self.hotel_date_selection_type.type_.name = "DEFAULT_HOTEL_DATE_NAME"
2220

23-
2421
self.hotel_advance_booking_window = MagicMock(name="hotel_advance_booking_window_attr")
2522
self.hotel_advance_booking_window.min_days = None
2623
self.hotel_advance_booking_window.max_days = None
@@ -37,28 +34,24 @@ def __init__(self, client_enums_mock): # Renamed to avoid conflict with client f
3734
self.hotel_check_in_date_range.start_date = None
3835
self.hotel_check_in_date_range.end_date = None
3936

40-
# This will store the name of the 'oneof' criterion field (e.g., "device")
41-
self._active_criterion_field = "device" # Default active field
37+
self._active_criterion_field = "device"
4238

43-
# This class method mimics the real .pb() class method on Google Ads model classes.
4439
@classmethod
4540
def pb(cls, instance_self):
46-
# instance_self here is an instance of MockAdGroupBidModifierModel
4741
mock_pb_message = MagicMock(name="pb_message_obj_from_class")
48-
# WhichOneof should return the active criterion field name for this instance
4942
mock_pb_message.WhichOneof.return_value = instance_self._active_criterion_field
5043
return mock_pb_message
5144

52-
# Helper to set the active criterion field for testing different types
5345
def set_active_criterion_field(self, field_name_str):
5446
self._active_criterion_field = field_name_str
55-
# Example: If setting device, ensure device attributes are primary
56-
if field_name_str == "device":
57-
# self.device.type_ is already a mock, its .name can be set in test
58-
pass
59-
# Add similar logic if other criterion types need specific default setup when activated
6047
return self
6148

49+
class MockGoogleAdsRow: # Newly added class
50+
def __init__(self):
51+
self.ad_group_bid_modifier = None
52+
self.ad_group = MagicMock(spec=['id'])
53+
self.campaign = MagicMock(spec=['id'])
54+
6255
# --- Test Functions ---
6356
def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
6457
"""Tests that the main function runs without raising an exception with an ad_group_id."""
@@ -68,47 +61,40 @@ def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
6861
# --- Mock Enums (ensure they have .name attribute) ---
6962
mock_enums = mock_google_ads_client.enums
7063

71-
# DeviceEnum setup
7264
mock_enums.DeviceEnum.MOBILE = MagicMock(name="DeviceEnum.MOBILE"); mock_enums.DeviceEnum.MOBILE.name = "MOBILE"
7365
mock_enums.DeviceEnum.TABLET = MagicMock(name="DeviceEnum.TABLET"); mock_enums.DeviceEnum.TABLET.name = "TABLET"
7466
mock_enums.DeviceEnum.DESKTOP = MagicMock(name="DeviceEnum.DESKTOP"); mock_enums.DeviceEnum.DESKTOP.name = "DESKTOP"
75-
mock_enums.DeviceEnum.UNKNOWN = MagicMock(name="DeviceEnum.UNKNOWN"); mock_enums.DeviceEnum.UNKNOWN.name = "UNKNOWN_DEVICE_FOR_PRINT" # Default in class constructor matched
67+
mock_enums.DeviceEnum.UNKNOWN = MagicMock(name="DeviceEnum.UNKNOWN"); mock_enums.DeviceEnum.UNKNOWN.name = "UNKNOWN_DEVICE_NAME"
7668

77-
# HotelDateSelectionTypeEnum setup
7869
mock_enums.HotelDateSelectionTypeEnum.DEFAULT_SELECTION = MagicMock(name="HotelDateSelectionTypeEnum.DEFAULT_SELECTION"); mock_enums.HotelDateSelectionTypeEnum.DEFAULT_SELECTION.name = "DEFAULT_SELECTION"
7970
mock_enums.HotelDateSelectionTypeEnum.USER_SELECTED = MagicMock(name="HotelDateSelectionTypeEnum.USER_SELECTED"); mock_enums.HotelDateSelectionTypeEnum.USER_SELECTED.name = "USER_SELECTED"
80-
mock_enums.HotelDateSelectionTypeEnum.UNKNOWN = MagicMock(name="HotelDateSelectionTypeEnum.UNKNOWN"); mock_enums.HotelDateSelectionTypeEnum.UNKNOWN.name = "UNKNOWN_HOTEL_DATE_FOR_PRINT"
71+
mock_enums.HotelDateSelectionTypeEnum.UNKNOWN = MagicMock(name="HotelDateSelectionTypeEnum.UNKNOWN"); mock_enums.HotelDateSelectionTypeEnum.UNKNOWN.name = "UNKNOWN_HOTEL_DATE_NAME"
8172

82-
# DayOfWeekEnum setup
8373
mock_enums.DayOfWeekEnum.MONDAY = MagicMock(name="DayOfWeekEnum.MONDAY"); mock_enums.DayOfWeekEnum.MONDAY.name = "MONDAY"
84-
mock_enums.DayOfWeekEnum.UNSPECIFIED = MagicMock(name="DayOfWeekEnum.UNSPECIFIED"); mock_enums.DayOfWeekEnum.UNSPECIFIED.name = "UNSPECIFIED_DAY_FOR_PRINT"
74+
mock_enums.DayOfWeekEnum.UNSPECIFIED = MagicMock(name="DayOfWeekEnum.UNSPECIFIED"); mock_enums.DayOfWeekEnum.UNSPECIFIED.name = "UNSPECIFIED_DAY_NAME"
8575

8676
# --- Mock GoogleAdsService for search ---
8777
mock_googleads_service = mock_google_ads_client.get_service("GoogleAdsService")
8878

8979
# Row 1: Device Modifier
90-
row1 = MagicMock(spec=['ad_group_bid_modifier', 'ad_group', 'campaign'])
91-
row1.campaign = MagicMock(spec=['id'])
80+
row1 = MockGoogleAdsRow() # Use MockGoogleAdsRow
9281
row1.campaign.id = "campaign1"
93-
row1.ad_group = MagicMock(spec=['id'])
9482
row1.ad_group.id = int(mock_ad_group_id)
9583

9684
modifier_device = MockAdGroupBidModifierModel(mock_google_ads_client.enums)
97-
modifier_device.criterion_id = "100"
85+
modifier_device.criterion_id = "test_crit_id_1"
9886
modifier_device.bid_modifier = 1.5
9987
modifier_device.set_active_criterion_field("device")
10088
modifier_device.device.type_ = mock_google_ads_client.enums.DeviceEnum.MOBILE
10189
row1.ad_group_bid_modifier = modifier_device
10290

10391
# Row 2: Hotel Check-in Day Modifier
104-
row2 = MagicMock(spec=['ad_group_bid_modifier', 'ad_group', 'campaign'])
105-
row2.campaign = MagicMock(spec=['id'])
92+
row2 = MockGoogleAdsRow() # Use MockGoogleAdsRow
10693
row2.campaign.id = "campaign2"
107-
row2.ad_group = MagicMock(spec=['id'])
10894
row2.ad_group.id = int(mock_ad_group_id)
10995

11096
modifier_hotel_day = MockAdGroupBidModifierModel(mock_google_ads_client.enums)
111-
modifier_hotel_day.criterion_id = "200"
97+
modifier_hotel_day.criterion_id = "test_crit_id_2"
11298
modifier_hotel_day.bid_modifier = 0.8
11399
modifier_hotel_day.set_active_criterion_field("hotel_check_in_day")
114100
modifier_hotel_day.hotel_check_in_day.day_of_week = mock_google_ads_client.enums.DayOfWeekEnum.MONDAY
@@ -130,24 +116,22 @@ def test_main_runs_successfully(mock_google_ads_client: MagicMock) -> None:
130116
def test_main_runs_without_ad_group_id(mock_google_ads_client: MagicMock) -> None:
131117
"""Tests that the main function runs without an ad_group_id (fetches for all ad groups)."""
132118
mock_customer_id = "123"
133-
mock_ad_group_id = None # Key difference for this test
119+
mock_ad_group_id = None
134120

135121
# --- Mock Enums (ensure they have .name attribute) ---
136122
mock_enums = mock_google_ads_client.enums
137123
mock_enums.DeviceEnum.TABLET = MagicMock(name="DeviceEnum.TABLET"); mock_enums.DeviceEnum.TABLET.name = "TABLET"
138-
mock_enums.DeviceEnum.UNKNOWN = MagicMock(name="DeviceEnum.UNKNOWN"); mock_enums.DeviceEnum.UNKNOWN.name = "UNKNOWN_DEVICE_FOR_PRINT" # Default in class constructor matched
124+
mock_enums.DeviceEnum.UNKNOWN = MagicMock(name="DeviceEnum.UNKNOWN"); mock_enums.DeviceEnum.UNKNOWN.name = "UNKNOWN_DEVICE_NAME"
139125

140126
# --- Mock GoogleAdsService for search ---
141127
mock_googleads_service = mock_google_ads_client.get_service("GoogleAdsService")
142128

143-
row1 = MagicMock(spec=['ad_group_bid_modifier', 'ad_group', 'campaign'])
144-
row1.campaign = MagicMock(spec=['id'])
129+
row1 = MockGoogleAdsRow() # Use MockGoogleAdsRow
145130
row1.campaign.id = "campaign3"
146-
row1.ad_group = MagicMock(spec=['id'])
147131
row1.ad_group.id = "987"
148132

149133
modifier1 = MockAdGroupBidModifierModel(mock_google_ads_client.enums)
150-
modifier1.criterion_id = "test_crit_id_3" # Using a more descriptive string for criterion ID
134+
modifier1.criterion_id = "test_crit_id_3"
151135
modifier1.bid_modifier = 1.2
152136
modifier1.set_active_criterion_field("device")
153137
modifier1.device.type_ = mock_google_ads_client.enums.DeviceEnum.TABLET
@@ -161,7 +145,7 @@ def test_main_runs_without_ad_group_id(mock_google_ads_client: MagicMock) -> Non
161145
main(
162146
mock_google_ads_client,
163147
mock_customer_id,
164-
mock_ad_group_id, # None
148+
mock_ad_group_id,
165149
)
166150
except Exception as e:
167151
pytest.fail(f"main function raised an exception: {e}")

0 commit comments

Comments
 (0)