I always clone my apps and test the modules locally. Unfortunately sometimes the process is not smooth and I can have the following problems:
- Sometimes server functions run on the server, so they can’t be debugged
- Sometimes server functions run locally, so they don’t work (for example when they use SQL - I have a dedicated plan with SQL access that only works on the server)
- Sometimes the production app, which runs on the server on a different environment and on a different branch, stops working while I run a test because its server functions run on my pc, where I am running the tests
A local test environment is easy to get to work, but I got it wrong so many times that I decided to come here and write down the steps required to:
- Have one module with server functions that always run on the server, and only their return value is tested
- Have one module with server functions that run locally and can also be debugged
- Not bother the production app while running the tests
Here are the steps to create an app with two server modules, one with the functions that should be tested locally and one with the functions that should run on the server even while testing:
- Create the app
- Create a branch called
dev
- Create 2 environments, Published with
master
and Development withdev
- In the Development branch click on “Show advanced settings” and enable server uplink. Make sure the “Send server calls from other environments to this Uplink” is unchecked (otherwise the production app would use your test Uplink while you test the Development branch)
- Make sure
dev
is checked out - Add two server modules and call them
RunOnServer
andRunEverywhere
- Add this function to
RunOnServer
:
@anvil.server.callable
def two():
txt = f'Two * {__name__} * {anvil.app.branch} * {anvil.server.context.type}'
return txt
- Add this function to
RunEverywhere
:
if anvil.server.context.type == 'uplink':
# running tests on pc
def two():
return anvil.server.call('two')
else:
# running on Anvil server
from RunOnServer import two
@anvil.server.callable
def one():
txt = f'One * {__name__} * {anvil.app.branch} * {anvil.server.context.type}'
return f'{txt}\n{two()}'
- Add this to
form_show
:
def form_show(self, **event_args):
one = anvil.server.call('one')
two = anvil.server.call('two')
alert(f'one:\n{one}\n\ntwo:\n{two}', large=True)
- Click on “Merge changes into master” (this will also check out
master
)
At this point running the app from dev
will show an alert saying it’s running from dev
, running it from master
will say from master
.
- Clone the app to a local repository
- Make sure
dev
is checked out - Add the
Tests
folder - Add the
test.py
file with this code, replacing<uplink key>
with the correct value:
import anvil.server
from unittest import TestCase
from server_code.RunEverywhere import one, two
class TestBackgroundTask(TestCase):
def test_1(self):
anvil.server.connect('<uplink key>')
print('=== one ===')
print(one())
print('=== two ===')
print(two())
At this point running the test the function one()
will run locally and the function two()
will run on the server.
I use type hinting on client modules. In the IDE it’s ignored. It doesn’t help, but doesn’t bother either. But it helps when developing locally, so I use it.
The problem is that type hinting doesn’t bother Skulpt, but the imports do.
The solution is to not import when running on the client.
This will import the typing classes when working locally with PyCharm but will not bother the browser:
if anvil.server.context.type != 'browser':
# running on server
from typing import List, Optional, Union, Tuple, Dict
Always make sure the “Send server calls from other environments to this Uplink” is unchecked. There may be cases where you want to run server functions on your uplink, but when that checkbox is checked, ALL the server functions defined in any running uplink ALWAYS run in the uplink.
This means that your production apps will pick uplink functions being tested and modified while the tests are connected and pick their own functions when all the uplinks have disconnected.
I had a few days hitting the head in the wall trying to figure out why an app was working for some users and not for other users, and for me was working only intermittently.
It is possible to debug HTTP endpoints by:
- checking “Send server calls from other environments to this Uplink”
- importing the module that defines them
- using the correct URL, which may not be the URL shown at the bottom of the server module that defines them in the IDE. The correct URL for the HTTP endpoints can be found by adding
_/api/
to the URL obtained by running the app from the IDE in its own tab
Careful: by checking the “Send server calls from other environments to this Uplink”, you are executing all the HTTP endpoints on your testing environment, even the production ones.