Add `expect_throws()` to `assert_utils.py` to check expected exception

Test: atest NetworkStaticLibHostPythonTests
Change-Id: I2e181f5b6f41e6de228d516dff970d47cfa3eaf6
diff --git a/staticlibs/tests/unit/host/python/assert_utils_test.py b/staticlibs/tests/unit/host/python/assert_utils_test.py
index 7a33373..1d85a12 100644
--- a/staticlibs/tests/unit/host/python/assert_utils_test.py
+++ b/staticlibs/tests/unit/host/python/assert_utils_test.py
@@ -14,7 +14,9 @@
 
 from mobly import asserts
 from mobly import base_test
-from net_tests_utils.host.python.assert_utils import UnexpectedBehaviorError, expect_with_retry
+from net_tests_utils.host.python.assert_utils import (
+    UnexpectedBehaviorError, UnexpectedExceptionError, expect_with_retry, expect_throws
+)
 
 
 class TestAssertUtils(base_test.BaseTestClass):
@@ -92,3 +94,22 @@
           retry_interval_sec=0,
       )
     asserts.assert_true(retry_action_called, "retry_action not called.")
+
+  def test_expect_exception_throws(self):
+      def raise_unexpected_behavior_error():
+          raise UnexpectedBehaviorError()
+
+      expect_throws(raise_unexpected_behavior_error, UnexpectedBehaviorError)
+
+  def test_unexpect_exception_throws(self):
+      def raise_value_error():
+          raise ValueError()
+
+      with asserts.assert_raises(UnexpectedExceptionError):
+          expect_throws(raise_value_error, UnexpectedBehaviorError)
+
+  def test_no_exception_throws(self):
+      def raise_no_error():
+          return
+
+      expect_throws(raise_no_error, UnexpectedBehaviorError)
\ No newline at end of file
diff --git a/staticlibs/testutils/host/python/assert_utils.py b/staticlibs/testutils/host/python/assert_utils.py
index da1bb9e..40094a2 100644
--- a/staticlibs/testutils/host/python/assert_utils.py
+++ b/staticlibs/testutils/host/python/assert_utils.py
@@ -19,6 +19,8 @@
 class UnexpectedBehaviorError(Exception):
   """Raised when there is an unexpected behavior during applying a procedure."""
 
+class UnexpectedExceptionError(Exception):
+  """Raised when there is an unexpected exception throws during applying a procedure"""
 
 def expect_with_retry(
     predicate: Callable[[], bool],
@@ -41,3 +43,17 @@
   raise UnexpectedBehaviorError(
       "Predicate didn't become true after " + str(max_retries) + " retries."
   )
+
+def expect_throws(runnable: callable, exception_class) -> None:
+  try:
+    runnable()
+    raise UnexpectedBehaviorError("Expected an exception, but none was thrown")
+  except exception_class:
+    pass
+  except UnexpectedBehaviorError as e:
+    raise e
+  except Exception as e:
+      raise UnexpectedExceptionError(
+        f"Expected exception of type {exception_class.__name__}, "
+        f"but got {type(e).__name__}: {e}"
+      )
\ No newline at end of file