2

I'm writing a unittest class to ensure a method tests for a success, and then tests for an Exception. I'm passing a response that should trigger the exception, but in the testing method it does not get raised. Of note, I can manually make the exception raise in the actual method.

Test class:

class TestAPI(TestCase):
    def test_send_method(self):
        with mock.patch('requests.post') as mock_request:
            mock_response = mock.Mock()
            mock_response.json.return_value = {
                "success": "true"
            }
            mock_request.return_value = mock_response
            send_method() // THIS WORKS NICELY

            # Test that errors from the API are handled correctly.
            with self.assertRaises(SendException):
                mock_response.status_code = 500
                mock_response.json.return_value = {
                    'errors': 'An error has occurred.',
                }
                send_method() // THIS RAISES NO EXCEPTION

As I said, It's odd because I can manually trigger the 500 status code in the actual method and it raises fine. I can even change the initial mock response success to err and it will raise in the actual method. Why would it not raise in the unittest?

Method being tested:

class SendException(Exception):
    pass

def send_method():
        session_headers = {
            "Content-Type": "application/json",
        }
        session_body = {
            "send": "yes"
        }
        session_url = u'{}'.format(URL)
        session_response = requests.post(session_url, json=session_body, headers=session_headers)
        try:
            if(session_response.json().get('errors') is not None):
                raise SendException(
                    'Returned error with status {}: {}'.format(
                        session_response.status_code,
                        session_response.json().get('errors')
                    )
                )
        except ValueError as err:
            raise SendException(
                'Responded with non-json and status code {}. Error: {} - Response Text: {}'.format(
                    session_response.status_code,
                    err,
                    session_response.text
                )
            )
  • We can't tell you without seeing the implementation of `test_send_method()`. – wim Jan 17 '18 at 01:35
  • I might be wrong here but I'm wondering...how should it reach the second `with` when you start recursion above it? – Darkonaut Jan 17 '18 at 01:36
  • @Darkonaut I'm actually noticing if I test one or the other it works. Testing both inside the the test method (as in the above code) I get the notice `AssertionError: SendException not raised` Why wouldn't it reach the second `with`? –  Jan 17 '18 at 01:39
  • @wim to save writing some adjusted pseudo code the exceptions in the send_method() run fine. Correct me if I'm wrong but I don't think you need the actual method. All I would show is a `requests.post` and then a `try: ... except` based on the response... As I stated, the exceptions are handled properly outside of the unittest. –  Jan 17 '18 at 01:43
  • Because you re-entry the method `test_send_method` again in your first `with` block and it will start all over again and again at that point? But I have no experience with mocks or django. Can you check if you get any result out of the second with block with print or run it in a debugger? – Darkonaut Jan 17 '18 at 01:51
  • @Darkonaut Sorry for the goose chase. That's a typo, I reference the actual send_method() IRL. Updated post. –  Jan 17 '18 at 01:59
  • I'm just guessing because we don't see how the exception might bubble up from `send_method`, but is it possible that it doesn't because you already handle the exception within a try/except in the code you didn't show us? – Darkonaut Jan 17 '18 at 02:56
  • @wim That makes sense. Added non-test method. –  Jan 17 '18 at 05:18
  • Sorry if this is a dumb question, but you are aware that `self.assertRaises(SendException)` will catch the `SendException`, right? The test will only fail if the method is *not* raising that exception. – wim Jan 17 '18 at 05:36
  • @wim yeah, it should test like that. But when I run it I get the error `AssertionError: SendException not raised` when it should be raised with the new session_response data. Which would then pass the test. –  Jan 17 '18 at 07:34
  • 1
    You could raise SendException manually in the line above `send_method() // THIS RAISES NO EXCEPTION` to test the unittest itself, it should pass. – Darkonaut Jan 17 '18 at 16:43
  • I think you forgot `mock_request.return_value = mock_response` above the second `send_method()` – Darkonaut Jan 17 '18 at 16:50
  • 1
    @Darkonaut as I see it, it's already assigned previously and that should remain stuck from before – wim Jan 17 '18 at 17:10
  • @wim I think that's exactly the problem that he never passes the changed `mock_response` to `mock request`. Changing `mock_response` alone does nothing because it's just a dull mock, `mock_request` is the patched object in the outer `with` block. – Darkonaut Jan 17 '18 at 17:24
  • @wim just found you're probably right after playing around with some mock snippets a little. This theory above would have fit so nicely to the behaviour he experiences. But I think at least I get now why they call it "mock" ;) – Darkonaut Jan 17 '18 at 19:03
  • Looking at your send_method...we don't actually know your mock returns anything when you call `send_method` do we? `send_method` just don't raises an exception but we don't know if json.return_value is "success": "true". Can you make `send_method` returning the json payload in the last line and than replace your first `send_method` with `assert mock_request.return_value == send_method()` to check if your payload makes the round trip? – Darkonaut Jan 17 '18 at 20:03
  • @Darkonaut I believe it is returning, but I have not confirmed that yet. For now I just turned them into independent tests, which is not very DRY but did the job till I can debug this further. Appreciate your input. –  Jan 17 '18 at 22:04
  • Please add your imports and file structure to your code. – Darkonaut Jan 18 '18 at 04:08

0 Answers0