How can I test a function that uses `anvil.server.request` outside of Anvil?

To unit-test a function that is decorated with anvil.server.http_endpoint, I want to mock anvil.server.request, as in the following minimal example:

import unittest
from unittest.mock import patch

import anvil.server


@anvil.server.http_endpoint("test-endpoint", methods=["POST"])
def function():
    return anvil.server.request.body_json


class TestFunction(unittest.TestCase):

    @patch("anvil.server.request")
    def test_function(self, mock_request):
        mock_request.return_value.body_json = [{"key": 1}]
        self.assertEqual({"key": 1}, function())

This raises Exception: anvil.server.request is only available in http_endpoint calls, however.

I have also tried the following:

  1. Patch anvil.server entirely. That solves this particular issue but any other anvil.server objects that are accessed by the function would need to be patched as well (e.g. accessing anvil.tables.app_tables) which is not feasible.
  2. Decorate the test function with @patch("anvil.server.request", new=mock_request), with mock_request being a simple object with a body_json attribute. The problem with this approach is that we have not found a way to avoid having each individual mock request object in the global namespace of our test suite, which is not ideal.

Is there a way to mock anvil.server.request as in my initial approach but not raise the exception? Because of all possible solutions, that would be the cleanest.

Instead of calling the function, can you import requests and call the http endpoint, as if it were running on another server?

I don’t think we can, no. As far as I understand, Anvil http endpoints only exist in the context of the running app, which is not the case when the tests are run as part of our CI/CD workflow.

I had not realized that.

In an http endpoint there are two things going on: the logic of the function and whatever has to be done for the http protocol.

There is no way to test the http part without the server, you would need to mock so much that you are not testing it.

So I would make the http endpoint very simple: get the data, pass it to a function that takes care of the logic and it’s part of the test suite, and return its return value.

The Anvil http endpoints try to convert to json, but sometimes they fail, mostly because of malformed requests. I stay away from the automatic json generation and manage the body myself. I can’t show the code now (I’m on my cell), but I always use the same few lines to do that untestable part of the job. They are always the same and I trust that they will always behave the same way.

Yeah, Isolating our own logic into a separate function and only writing tests against that function is actually how we did proceed in the mean time. Maybe I should have added that bit of info to the original post.

Thanks, seems like that is the way to go then!