Search code examples
pythondjangounit-testingseleniumbdd

Set up Django DiscoverRunner to always recreate database on testing with radish


I am using radish bdd with selenium to test my django app, however sometimes django ask to delete database because it's already exists in database. here's my terrain.py:

import os

import django
from django.test.runner import DiscoverRunner
from django.test import LiveServerTestCase
from radish import before, after
from selenium import webdriver


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tangorblog.settings.features')
BASE_URL = os.environ.get('BASE_URL', 'http://localhost:8000')


@before.each_scenario
def setup_django_test(scenario):
    django.setup()
    scenario.context.test_runner = DiscoverRunner()
    scenario.context.test_runner.setup_test_environment()
    scenario.context.old_db_config =\
        scenario.context.test_runner.setup_databases()
    scenario.context.base_url = BASE_URL
    scenario.context.test_case = LiveServerTestCase()
    scenario.context.test_case.setUpClass()
    scenario.context.browser = webdriver.Chrome()


@after.each_scenario
def teardown_django(scenario):
    scenario.context.browser.quit()
    scenario.context.test_case.tearDownClass()
    del scenario.context.test_case
    scenario.context.test_runner.teardown_databases(
        scenario.context.old_db_config)
    scenario.context.test_runner.teardown_test_environment()

I think that, I can somehow could alter this on

scenario.context.old_db_config =\
            scenario.context.test_runner.setup_databases()

But I don't know how. Any help?


Solution

  • @Wyatt, again I'm just gonna modify your answer. I have try and run your solution, however it didn't manage to make each scenario independent, I even encounter an Integrity error when I try to make model object inside scenario. Regardless I still use your solution (especially RadishTestRunner, as the idea comes from you. I modified it so I could run django unittest separately from radish. I use LiveServerTestCase directly and remove LiveServer as I notice the similirity between the two, except that LiveServerTestCase inherits from TransactionTestCase and it also has the LiveServerThread and _StaticFilesHandler built in. Here's how it is:

    # package/test/runner.py
    import os
    from django.test.runner import DiscoverRunner
    import radish.main
    
    
    class RadishTestRunner(DiscoverRunner):
        radish_features = ['features']
        def run_suite(self, suite, **kwargs):
            # run radish test
            return radish.main.main(self.radish_features)
    
        def suite_result(self, suite, result, **kwargs):
            return result
    
        def set_radish_features(self, features):
            self.radish_features = features
    
    # radish/world.py
    from django.test import LiveServerTestCase
    
    
    from radish import pick
    
    from selenium import webdriver
    
    
    @pick
    def get_browser():
        return webdriver.Chrome()
    
    
    @pick
    def get_live_server():
        live_server = LiveServerTestCase
        live_server.setUpClass()
        return live_server
    
    # radish/terrain.py
    from radish import world, before, after
    from selenium import webdriver
    
    
    @before.all
    def set_up(features, marker):
        world.get_live_server()
    
    
    @after.all
    def tear_down(features, marker):
        live_server = world.get_live_server()
        live_server.tearDownClass()
    
    
    @before.each_scenario
    def set_up_scenario(scenario):
        live_server = world.get_live_server()
    
        scenario.context.browser = webdriver.Chrome()
        scenario.context.base_url = live_server.live_server_url
    
        scenario.context.test_case = live_server()
        scenario.context.test_case._pre_setup()
    
    
    @after.each_scenario
    def tear_down_scenario(scenario):
        scenario.context.test_case._post_teardown()
        scenario.context.browser.quit()
    

    That's it. This also fix the problem with PostgreSQL on my other question that you point out. I also open and quit browser on each scenario, as it gives me more control over the browser inside scenario. Thank you so much for you effort to point me at the right direction.

    Finally I return to PostgreSQL. PostgreSQL seem to better than MySQL in terms of speed. It greatly reduced the time to run test.

    And oh ya, I need to run ./manage.py collectstatic first after specify STATIC_ROOT in django settings file.

    I also modify RadishTestRunner, so instead of running with RADISH_ONLY=1, I could run it with python manage.py radish /path/to/features/file. Here's my radish command:

    # package.management.commands.radish
    from __future__ import absolute_import
    import sys
    from django.core.management.base import BaseCommand, CommandError
    from package.test.runner import RadishTestRunner
    
    
    class Command(BaseCommand):
        def add_arguments(self, parser):
            parser.add_argument('features', nargs='+', type=str)
    
        def handle(self, *args, **options):
            test_runner = RadishTestRunner(interactive=False)
            if options['features']:
                test_runner.set_radish_features(options['features'])
            result = test_runner.run_suite(None)
            if result:
                sys.exit(result)
    

    By using radish with django management command, we have control over which feature file we want to run.