Django automated runserver and functional test

Question:

Im trying to use invoke module to automate my django functional tests, but I realised as the runserver command is rolling on my script waits until its finished to run the next command

from invoke import task

    @task
    def runserver(c):
        c.run('python manage.py runserver')
    
    @task
    def test(c):
        c.run('python functional_test.py')
    
    @task(runserver, test)
    def build(c):
        pass

I wanted to run both simultaneously, maybe with async or threding but cant figure out how.

Asked By: Kabllez

||

Answers:

I ended up using the following code, with the idea @Nealium gave but with Pebble module:

from time import sleep
from invoke import task
from pebble import concurrent
import os

@task
def runserver(c,ip=None):
    if ip:
        ip_addr = ip
    else:
        ip_addr = None
    @concurrent.thread
    def build(c):
        if ip_addr:
            c.run(f'python manage.py runserver {ip_addr}:8091')
        else:
            c.run('python manage.py runserver')
        sleep(3)
                    
    @concurrent.thread
    def start():
        with open('build.txt','w') as file:
            file.write("Build done")
        c.run('python functional_test.py')
    
    build_future = build()
    start_future = start()
    
    build_done = False
    while not build_done:
        sleep(1)
        try:
            with open('build.txt', 'r') as file:
                content = file.read()
                if 'Build done' in content:
                    build_future.cancel()
                    build_done = True
        except:
            pass
    os.remove('build.txt')
    start_future.result()
  • Like this we can just call in terminal "invoke runserver" or "inv
    runserver" (or change it to "build" if you like) and it will do what
    it has to do and finish the test+server. I had to do the "build.txt"
    file because there’s two terminals (django server and the test) and one
    needed to know when the other was up.
  • We can also pass –ip arg, like: "invoke runserver –ip=0.0.0.0", the
    port its fixed but you got the idea

EDIT 1: Turns out Django already provided want I wanted with LiveServerTestCase class. I just didnt want to open two terminals (one to run the server locally and another to type "python manage.py test") as I intend to make a CI workflow

from django.test import LiveServerTestCase

class LoginViewTest(LiveServerTestCase):

    def setUp(self):
        options = Options()
        options.add_argument('-headless')
        manager = GeckoDriverManager().install()
        driver = webdriver.Firefox(options=options, service=FirefoxService(manager))
        self.driver = driver
        self.user = User.objects.create_user(
            username='testuser',
            email='[email protected]',
            password='testpassword'
        )

    def tearDown(self):
        self.driver.quit()

    def test_login_sucess(self):
        #Trying to reach the login page
        self.driver.get(self.live_server_url + '/login')
        
        #Assert login title is correct 
        self.assertEqual(self.driver.title,'Login')

        #[... continues test]
Answered By: Kabllez
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.