Skip to content

Commit a4b4a92

Browse files
authored
Merge pull request #16 from googleads/verify_tests
Fixed minor bugs in api_examples tests and added test for conversion upload summary
2 parents 2b7b2bf + 15dcbe6 commit a4b4a92

4 files changed

Lines changed: 121 additions & 11 deletions

File tree

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* 1.4.1
2+
- Fixed minor bugs in tests for api_examples.
3+
- Added test api_examples/tests/test_get_conversion_upload_summary.py
4+
15
* 1.4.0
26
- Modified ChangeLog so changes are listed in reverse chronological order.
37
- Added step_by_step custom command.

api_examples/remove_automatically_created_assets.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717

1818
from google.ads.googleads.client import GoogleAdsClient
1919
from google.ads.googleads.errors import GoogleAdsException
20-
from google.ads.googleads.v22.enums.asset_field_type_enum.asset_field_type import (
21-
AssetFieldTypeEnum,
22-
)
20+
from google.ads.googleads.v22.enums import AssetFieldTypeEnum
2321

2422

2523
def main(
@@ -54,11 +52,11 @@ def main(
5452
# removing (e.g., TEXT, IMAGE, VIDEO).
5553

5654
try:
57-
field_type_enum = getattr(AssetFieldTypeEnum, field_type.upper())
55+
field_type_enum = getattr(AssetFieldTypeEnum.AssetFieldType, field_type.upper())
5856
except AttributeError:
5957
print(
6058
f"Error: Invalid field type '{field_type}'. "
61-
f"Please use one of: {[e.name for e in AssetFieldTypeEnum if e.name not in ('UNSPECIFIED', 'UNKNOWN')]}"
59+
f"Please use one of: {[e.name for e in AssetFieldTypeEnum.AssetFieldType if e.name not in ('UNSPECIFIED', 'UNKNOWN')]}"
6260
)
6361
sys.exit(1)
6462

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import sys
16+
import os
17+
import unittest
18+
from unittest.mock import MagicMock, call
19+
from io import StringIO
20+
21+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../")))
22+
23+
from google.ads.googleads.errors import GoogleAdsException
24+
from google.ads.googleads.client import GoogleAdsClient
25+
from api_examples.get_conversion_upload_summary import main
26+
27+
28+
class TestGetConversionUploadSummary(unittest.TestCase):
29+
def setUp(self):
30+
self.mock_client = MagicMock(spec=GoogleAdsClient)
31+
self.mock_ga_service = MagicMock()
32+
self.mock_client.get_service.return_value = self.mock_ga_service
33+
self.customer_id = "1234567890"
34+
35+
self.captured_output = StringIO()
36+
sys.stdout = self.captured_output
37+
38+
def tearDown(self):
39+
sys.stdout = sys.__stdout__
40+
41+
def test_main_success(self):
42+
# Mock responses for search_stream
43+
mock_batch_1 = MagicMock()
44+
mock_row_1 = MagicMock()
45+
mock_summary_1 = MagicMock()
46+
mock_summary_1.resource_name = "customers/123/offlineConversionUploadClientSummaries/1"
47+
mock_summary_1.status.name = "SUCCESS"
48+
mock_summary_1.total_event_count = 10
49+
mock_summary_1.successful_event_count = 10
50+
mock_summary_1.success_rate = 1.0
51+
mock_summary_1.last_upload_date_time = "2024-01-01 12:00:00"
52+
mock_summary_1.alerts = []
53+
mock_summary_1.daily_summaries = []
54+
mock_summary_1.job_summaries = []
55+
mock_row_1.offline_conversion_upload_client_summary = mock_summary_1
56+
mock_batch_1.results = [mock_row_1]
57+
58+
mock_batch_2 = MagicMock()
59+
mock_row_2 = MagicMock()
60+
mock_summary_2 = MagicMock()
61+
mock_summary_2.resource_name = "customers/123/offlineConversionUploadConversionActionSummaries/1"
62+
mock_summary_2.conversion_action_name = "My Conversion Action"
63+
mock_summary_2.status.name = "SUCCESS"
64+
mock_summary_2.total_event_count = 5
65+
mock_summary_2.successful_event_count = 5
66+
mock_summary_2.alerts = []
67+
mock_summary_2.daily_summaries = []
68+
mock_summary_2.job_summaries = []
69+
mock_row_2.offline_conversion_upload_conversion_action_summary = mock_summary_2
70+
mock_batch_2.results = [mock_row_2]
71+
72+
# The first call returns client summary, second call returns conversion action summary
73+
self.mock_ga_service.search_stream.side_effect = [[mock_batch_1], [mock_batch_2]]
74+
75+
main(self.mock_client, self.customer_id)
76+
77+
# Check output
78+
output = self.captured_output.getvalue()
79+
self.assertIn("Offline Conversion Upload Client Summary:", output)
80+
self.assertIn("Resource Name: customers/123/offlineConversionUploadClientSummaries/1", output)
81+
self.assertIn("Offline Conversion Upload Conversion Action Summary:", output)
82+
self.assertIn("Conversion Action Name: My Conversion Action", output)
83+
84+
self.assertEqual(self.mock_ga_service.search_stream.call_count, 2)
85+
86+
def test_main_google_ads_exception(self):
87+
mock_error = MagicMock()
88+
mock_error.code.return_value.name = "INTERNAL_ERROR"
89+
mock_failure = MagicMock()
90+
mock_failure.errors = [MagicMock(message="Internal error")]
91+
92+
self.mock_ga_service.search_stream.side_effect = GoogleAdsException(
93+
error=mock_error,
94+
call=MagicMock(),
95+
failure=mock_failure,
96+
request_id="test_request_id"
97+
)
98+
99+
with self.assertRaises(SystemExit) as cm:
100+
main(self.mock_client, self.customer_id)
101+
102+
self.assertEqual(cm.exception.code, 1)
103+
output = self.captured_output.getvalue()
104+
self.assertIn('Request with ID "test_request_id" failed with status "INTERNAL_ERROR"', output)
105+
106+
if __name__ == "__main__":
107+
unittest.main()

api_examples/tests/test_remove_automatically_created_assets.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def setUp(self):
3636

3737
self.mock_client.get_service.side_effect = self._get_mock_service
3838

39+
self.patcher = unittest.mock.patch(
40+
"api_examples.remove_automatically_created_assets.AssetFieldTypeEnum"
41+
)
42+
self.mock_asset_field_type_enum = self.patcher.start()
43+
3944
class MockAssetFieldType:
4045
UNSPECIFIED = MagicMock()
4146
UNSPECIFIED.name = "UNSPECIFIED"
@@ -67,13 +72,8 @@ def __iter__(self):
6772
]
6873
)
6974

70-
self.mock_real_asset_field_type = MockAssetFieldType()
75+
self.mock_asset_field_type_enum.AssetFieldType = MockAssetFieldType()
7176

72-
self.mock_client.enums = MagicMock()
73-
self.mock_client.enums.AssetFieldTypeEnum = MagicMock()
74-
self.mock_client.enums.AssetFieldTypeEnum.AssetFieldType = (
75-
self.mock_real_asset_field_type
76-
)
7777

7878
self.customer_id = "1234567890"
7979
self.campaign_id = 12345
@@ -92,6 +92,7 @@ def _get_mock_service(self, service_name):
9292

9393
def tearDown(self):
9494
sys.stdout = sys.__stdout__
95+
self.patcher.stop()
9596

9697
def test_main_successful_removal(self):
9798
mock_response = MagicMock()

0 commit comments

Comments
 (0)