Skip to content

chore(optimizer): Add unit tests for Optimizer KubernetesBackend#334

Open
ruskaruma wants to merge 1 commit intokubeflow:mainfrom
ruskaruma:test/optimizer-kubernetes-backend1
Open

chore(optimizer): Add unit tests for Optimizer KubernetesBackend#334
ruskaruma wants to merge 1 commit intokubeflow:mainfrom
ruskaruma:test/optimizer-kubernetes-backend1

Conversation

@ruskaruma
Copy link
Contributor

What this PR does / why we need it:
Adds unit tests for the Optimizer KubernetesBackend, covering all 8 public methods: optimize, list_jobs, get_job, get_job_logs, get_best_results, wait_for_job_status, delete_job, and get_job_events. Addresses
review feedback from #224 which went stale.

Also renames the existing test_optimize (input mutation test) to test_optimize_no_input_mutation to avoid name collision with the new test_optimize that verifies Experiment CR creation.

Which issue(s) this PR fixes:
Fixes #126

Checklist:

  • Unit tests included (30 tests, all passing)
  • Docs included if any changes are user facing

Copilot AI review requested due to automatic review settings February 26, 2026 18:55
@google-oss-prow
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign astefanutti for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ruskaruma ruskaruma changed the title test(optimizer): Add unit tests for Optimizer KubernetesBackend (#126) chore(optimizer): Add unit tests for Optimizer KubernetesBackend Feb 26, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds comprehensive unit tests for Optimizer’s KubernetesBackend to cover all public methods and expands mocked Kubernetes/Katib interactions to support those scenarios.

Changes:

  • Added fixtures + mock handlers to simulate Katib Experiment/Trial CRUD, events, and logs.
  • Added new parametrized tests covering optimize, job lifecycle methods, logs, results, polling, deletion, and events.
  • Renamed prior test_optimize to test_optimize_no_input_mutation to avoid a name collision.

)
def test_optimize(kubernetes_backend, test_case):
"""Test KubernetesBackend.optimize and Experiment CR creation."""
print("Executing test:", test_case.name)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest captures stdout; these print() calls add noise and can make failures harder to scan. Prefer removing them, or use pytest’s reporting mechanisms (e.g., -vv already shows parametrized case IDs) / caplog if you need diagnostic output.

Copilot uses AI. Check for mistakes.

except Exception as e:
assert type(e) is test_case.expected_error
print("test execution complete")
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest captures stdout; these print() calls add noise and can make failures harder to scan. Prefer removing them, or use pytest’s reporting mechanisms (e.g., -vv already shows parametrized case IDs) / caplog if you need diagnostic output.

Copilot uses AI. Check for mistakes.
)
def test_list_jobs(kubernetes_backend, test_case):
"""Test KubernetesBackend.list_jobs returns expected OptimizationJob list."""
print("Executing test:", test_case.name)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest captures stdout; these print() calls add noise and can make failures harder to scan. Prefer removing them, or use pytest’s reporting mechanisms (e.g., -vv already shows parametrized case IDs) / caplog if you need diagnostic output.

Copilot uses AI. Check for mistakes.

except Exception as e:
assert type(e) is test_case.expected_error
print("test execution complete")
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest captures stdout; these print() calls add noise and can make failures harder to scan. Prefer removing them, or use pytest’s reporting mechanisms (e.g., -vv already shows parametrized case IDs) / caplog if you need diagnostic output.

Copilot uses AI. Check for mistakes.
)
def test_get_job(kubernetes_backend, test_case):
"""Test KubernetesBackend.get_job with success and error paths."""
print("Executing test:", test_case.name)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest captures stdout; these print() calls add noise and can make failures harder to scan. Prefer removing them, or use pytest’s reporting mechanisms (e.g., -vv already shows parametrized case IDs) / caplog if you need diagnostic output.

Copilot uses AI. Check for mistakes.
Comment on lines +914 to +915
except Exception as e:
assert type(e) is test_case.expected_error
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using type(e) is ... is brittle and fails for subclasses; it also makes it harder to accept wrapped exceptions (common around K8s client calls). Prefer isinstance(e, test_case.expected_error) and, where helpful, assert on message/context separately.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +154
def conditional_error_handler(*args, **kwargs):
"""Raise simulated errors based on resource name."""
if args[2] == TIMEOUT:
raise multiprocessing.TimeoutError()
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mocks raise multiprocessing.TimeoutError, but the parametrized cases expect TimeoutError. That makes the tests rely on an implicit conversion inside the backend (or they’ll break if that behavior changes). Consider raising built-in TimeoutError directly in these mock handlers (or update expected exception types consistently) so the tests assert the backend behavior rather than a specific internal translation path.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +163
def get_namespaced_custom_object_response(*args, **kwargs):
"""Return a mocked Experiment CR via mock async thread."""
mock_thread = Mock()
if args[2] == TIMEOUT or args[4] == TIMEOUT:
raise multiprocessing.TimeoutError()
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mocks raise multiprocessing.TimeoutError, but the parametrized cases expect TimeoutError. That makes the tests rely on an implicit conversion inside the backend (or they’ll break if that behavior changes). Consider raising built-in TimeoutError directly in these mock handlers (or update expected exception types consistently) so the tests assert the backend behavior rather than a specific internal translation path.

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +175
def list_namespaced_custom_object_response(*args, **kwargs):
"""Return ExperimentList or TrialList via mock async thread."""
mock_thread = Mock()
if args[2] == TIMEOUT:
raise multiprocessing.TimeoutError()
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mocks raise multiprocessing.TimeoutError, but the parametrized cases expect TimeoutError. That makes the tests rely on an implicit conversion inside the backend (or they’ll break if that behavior changes). Consider raising built-in TimeoutError directly in these mock handlers (or update expected exception types consistently) so the tests assert the backend behavior rather than a specific internal translation path.

Copilot uses AI. Check for mistakes.

assert test_case.expected_status == SUCCESS
assert isinstance(name, str)
assert len(name) == 12
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserting an exact generated name length is a fragile contract for a unit test (small changes to name generation can cause unrelated failures). Prefer asserting properties that matter to behavior (e.g., non-empty, DNS-1123-compliant if required, and that the generated name is used in the Experiment metadata passed to create_namespaced_custom_object).

Suggested change
assert len(name) == 12
assert len(name) > 0

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create unit tests for the OptimizerClient()

2 participants