diff --git a/kgb/agency.py b/kgb/agency.py
index 9ecbbcdc7e25bf5e8e1f0fb69defe8eab16ea980..76d4477e213701bcc6def1e445e42c225fdd8ccd 100644
--- a/kgb/agency.py
+++ b/kgb/agency.py
@@ -189,6 +189,36 @@ class SpyAgency(object):
             self._kgb_assert_fail('%s was not called.'
                                   % self._format_spy_or_call(spy))
 
+    def assertSpyCalledOnce(self, spy):
+        """Assert that a function has been called exactly once.
+
+        This will imply :py:meth:`assertHasSpy`.
+
+        Args:
+            spy (callable or kgb.spies.FunctionSpy):
+                The function or spy to check.
+
+        Raises:
+            AssertionError:
+                The function was not called.
+        """
+        self.assertSpyCalled(spy)
+
+        # The spy's been called at least once. If it's called more than once,
+        # show the calls.
+        call_count = len(spy.calls)
+
+        if call_count > 1:
+            self._kgb_assert_fail(
+                '%s was not called exactly 1 time. It was called %d times:\n'
+                '\n'
+                '%s'
+                % (
+                    self._format_spy_or_call(spy),
+                    call_count,
+                    self._format_spy_calls(spy, self._format_spy_call_args),
+                ))
+
     def assertSpyNotCalled(self, spy):
         """Assert that a function has not been called.
 
@@ -314,6 +344,60 @@ class SpyAgency(object):
                             self._format_spy_call_args),
                     ))
 
+    def assertSpyCalledOnceWith(self, spy_or_call, *expected_args,
+                                **expected_kwargs):
+        """Assert that a function was called once with the given arguments.
+
+        This will fail if there was more than one call to the function.
+
+        This will imply :py:meth:`assertSpyCalledOnce`.
+
+        Args:
+            spy_or_call (callable or kgb.spies.FunctionSpy):
+                The function, spy, or call to check.
+
+            *expected_args (tuple):
+                Position arguments expected to be provided in any of the calls.
+
+            **expected_kwargs (dict):
+                Keyword arguments expected to be provided in any of the calls.
+
+        Raises:
+            AssertionError:
+                The function was not called with the provided arguments.
+        """
+        self.assertSpyCalledOnce(spy_or_call)
+
+        if not spy_or_call.called_with(*expected_args, **expected_kwargs):
+            if isinstance(spy_or_call, SpyCall):
+                self._kgb_assert_fail(
+                    'This call to %s was not passed args=%s, kwargs=%s.\n'
+                    '\n'
+                    'It was called with:\n'
+                    '\n'
+                    '%s'
+                    % (
+                        self._format_spy_or_call(spy_or_call),
+                        safe_repr(expected_args),
+                        format_spy_kwargs(expected_kwargs),
+                        self._format_spy_call_args(spy_or_call),
+                    ))
+            else:
+                self._kgb_assert_fail(
+                    'No call to %s was passed args=%s, kwargs=%s.\n'
+                    '\n'
+                    'The following calls were recorded:\n'
+                    '\n'
+                    '%s'
+                    % (
+                        self._format_spy_or_call(spy_or_call),
+                        safe_repr(expected_args),
+                        format_spy_kwargs(expected_kwargs),
+                        self._format_spy_calls(
+                            spy_or_call,
+                            self._format_spy_call_args),
+                    ))
+
     def assertSpyNotCalledWith(self, spy_or_call, *expected_args,
                                **expected_kwargs):
         """Assert that a function was not called with the given arguments.
@@ -893,9 +977,11 @@ class SpyAgency(object):
     # Useful for pytest and other uses.
     assert_has_spy = assertHasSpy
     assert_spy_called = assertSpyCalled
+    assert_spy_called_once = assertSpyCalledOnce
     assert_spy_not_called = assertSpyNotCalled
     assert_spy_call_count = assertSpyCallCount
     assert_spy_called_with = assertSpyCalledWith
+    assert_spy_called_once_with = assertSpyCalledOnceWith
     assert_spy_not_called_with = assertSpyNotCalledWith
     assert_spy_last_called_with = assertSpyLastCalledWith
     assert_spy_returned = assertSpyReturned
diff --git a/kgb/tests/test_spy_agency.py b/kgb/tests/test_spy_agency.py
index a53cbe8c7676e49dd18e5ea437b9341fd433f86f..65247017de78b6491fcd12f65d7f5a768bb9c3b3 100644
--- a/kgb/tests/test_spy_agency.py
+++ b/kgb/tests/test_spy_agency.py
@@ -240,6 +240,129 @@ class TestCaseMixinTests(SpyAgency, TestCase):
         with self._check_assertion('do_math was called 2 times, not 3.'):
             self.assertSpyCallCount(obj.do_math.spy, 3)
 
+    def test_assertSpyCalledOnce_with_0_calls(self):
+        """Testing SpyAgency.assertSpyCalledOnce with 0 calls"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        # This should fail.
+        msg = 'do_math was not called.'
+
+        with self._check_assertion(msg):
+            self.assertSpyCalledOnce(obj.do_math)
+
+    def test_assertSpyCalledOnce_with_1_calls(self):
+        """Testing SpyAgency.assertSpyCalledOnce with 1 call"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        obj.do_math(1, b=4)
+
+        # These should not fail.
+        self.assertSpyCalledOnce(obj.do_math)
+        self.assertSpyCalledOnce(obj.do_math.spy)
+
+        # Check the aliases.
+        self.assert_spy_called_once(obj.do_math)
+        kgb.asserts.assert_spy_called_once(obj.do_math)
+
+    def test_assertSpyCalledOnce_with_2_calls(self):
+        """Testing SpyAgency.assertSpyCalledOnce with 2 calls"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        obj.do_math(1, b=4)
+        obj.do_math(2, b=8)
+
+        # This should fail.
+        msg = (
+            "do_math was not called exactly 1 time. It was called 2 times:\n"
+            "\n"
+            "Call 0:\n"
+            "  args=()\n"
+            "  kwargs={'a': 1, 'b': 4}\n"
+            "\n"
+            "Call 1:\n"
+            "  args=()\n"
+            "  kwargs={'a': 2, 'b': 8}"
+        )
+
+        with self._check_assertion(msg):
+            self.assertSpyCalledOnce(obj.do_math)
+
+    def test_assertSpyCalledOnceWith_with_0_calls(self):
+        """Testing SpyAgency.assertSpyCalledOnceWith with 0 calls"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        # This should fail.
+        msg = 'do_math was not called.'
+
+        with self._check_assertion(msg):
+            self.assertSpyCalledOnceWith(obj.do_math, a=1, b=2)
+
+    def test_assertSpyCalledOnceWith_with_1_call(self):
+        """Testing SpyAgency.assertSpyCalledOnceWith with 1 call"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        obj.do_math(1, b=4)
+
+        # This should not fail.
+        self.assertSpyCalledOnceWith(obj.do_math, a=1, b=4)
+        self.assertSpyCalledOnceWith(obj.do_math.spy, a=1, b=4)
+
+        # Check the aliases.
+        self.assert_spy_called_once_with(obj.do_math, a=1, b=4)
+        kgb.asserts.assert_spy_called_once_with(obj.do_math, a=1, b=4)
+
+    def test_assertSpyCalledOnceWith_with_1_calls_with_wrong_args(self):
+        """Testing SpyAgency.assertSpyCalledOnceWith with 1 call with wrong
+        arguments
+        """
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        obj.do_math(1, b=4)
+
+        # This should fail.
+        msg = (
+            "No call to do_math was passed args=(1,), kwargs={'b': 8}.\n"
+            "\n"
+            "The following calls were recorded:\n"
+            "\n"
+            "Call 0:\n"
+            "  args=()\n"
+            "  kwargs={'a': 1, 'b': 4}"
+        )
+
+        with self._check_assertion(msg):
+            self.assertSpyCalledOnceWith(obj.do_math, 1, b=8)
+
+    def test_assertSpyCalledOnceWith_with_2_calls(self):
+        """Testing SpyAgency.assertSpyCalledOnceWith with 2 calls"""
+        obj = MathClass()
+        self.spy_on(obj.do_math)
+
+        obj.do_math(1, b=4)
+        obj.do_math(2, b=8)
+
+        # This should fail.
+        msg = (
+            "do_math was not called exactly 1 time. It was called 2 times:\n"
+            "\n"
+            "Call 0:\n"
+            "  args=()\n"
+            "  kwargs={'a': 1, 'b': 4}\n"
+            "\n"
+            "Call 1:\n"
+            "  args=()\n"
+            "  kwargs={'a': 2, 'b': 8}"
+        )
+
+        with self._check_assertion(msg):
+            self.assertSpyCalledOnceWith(obj.do_math, 1, b=4)
+
     def test_assertSpyCalledWith_with_expected_arguments(self):
         """Testing SpyAgency.assertSpyCalledWith with expected arguments"""
         obj = MathClass()
