You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中随机打乱并运行指定数量的测试子集?

需求说明

我们基于Django开展测试工作,现有约6000个测试用例,全量运行耗时约40分钟。需要实现以下功能:

  • 通过命令行参数指定要运行的随机测试用例数量(例如200,可自定义)
  • 该参数需支持与Django自带的--shuffle参数配合使用
  • 若总测试用例数少于指定数量,则自动运行全部测试

现有代码

speedy/core/base/management/commands/test.py

from django.core.management.commands import test


class Command(test.Command):
    def add_arguments(self, parser):
        super().add_arguments(parser=parser)
        parser.add_argument(
            "--test-all-languages",
            action="store_true",
            help="If run with this argument, test all languages, and don't skip languages.",
        )

speedy/core/base/test/models.py

class SiteDiscoverRunner(DiscoverRunner):
        def __init__(self, *args, **kwargs):
            assert (django_settings.TESTS is True)
            super().__init__(*args, **kwargs)
            self.test_all_languages = kwargs.get('test_all_languages', False)

        def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
            if (not (test_labels)):
                # Default test_labels are all the relevant directories under "speedy". For example ["speedy.core", "speedy.net"].
                # Due to problems with templates, "speedy.match" label is not added to speedy.net tests, and "speedy.net" label is not added to speedy.match tests. # ~~~~ TODO: fix this bug and enable these labels, although the tests there are skipped.
                test_labels = []
                for label in django_settings.INSTALLED_APPS:
                    if (label.startswith('speedy.')):
                        label_to_test = '.'.join(label.split('.')[:2])
                        if (label_to_test == 'speedy.net'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_NET_SITE_ID)
                        elif (label_to_test == 'speedy.match'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID)
                        elif (label_to_test == 'speedy.composer'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_COMPOSER_SITE_ID)
                        elif (label_to_test == 'speedy.mail'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_MAIL_SOFTWARE_SITE_ID)
                        else:
                            add_this_label = True
                        if (add_this_label):
                            if (not (label_to_test in test_labels)):
                                test_labels.append(label_to_test)
            print(test_labels)
            return super().build_suite(test_labels=test_labels, extra_tests=extra_tests, **kwargs)

        def setup_test_environment(self, **kwargs):
            super().setup_test_environment(**kwargs)
            django_settings.TEST_ALL_LANGUAGES = self.test_all_languages

        def teardown_test_environment(self, **kwargs):
            super().teardown_test_environment(**kwargs)
            del django_settings.TEST_ALL_LANGUAGES

实现方案

1. 添加命令行参数

修改speedy/core/base/management/commands/test.py,新增--random-count参数:

from django.core.management.commands import test


class Command(test.Command):
    def add_arguments(self, parser):
        super().add_arguments(parser=parser)
        parser.add_argument(
            "--test-all-languages",
            action="store_true",
            help="If run with this argument, test all languages, and don't skip languages.",
        )
        # 新增随机测试数量参数
        parser.add_argument(
            "--random-count",
            type=int,
            default=None,
            help="Run a random subset of N test cases. If N exceeds total tests, run all. Compatible with --shuffle.",
        )

2. 修改测试运行器逻辑

修改speedy/core/base/test/models.py中的SiteDiscoverRunner,添加随机抽取测试用例的逻辑:

import random
from django.test import TestSuite

    class SiteDiscoverRunner(DiscoverRunner):
        def __init__(self, *args, **kwargs):
            assert (django_settings.TESTS is True)
            super().__init__(*args, **kwargs)
            self.test_all_languages = kwargs.get('test_all_languages', False)
            # 接收随机数量参数
            self.random_count = kwargs.get('random_count', None)

        def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
            if (not (test_labels)):
                test_labels = []
                for label in django_settings.INSTALLED_APPS:
                    if (label.startswith('speedy.')):
                        label_to_test = '.'.join(label.split('.')[:2])
                        if (label_to_test == 'speedy.net'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_NET_SITE_ID)
                        elif (label_to_test == 'speedy.match'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_MATCH_SITE_ID)
                        elif (label_to_test == 'speedy.composer'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_COMPOSER_SITE_ID)
                        elif (label_to_test == 'speedy.mail'):
                            add_this_label = (django_settings.SITE_ID == django_settings.SPEEDY_MAIL_SOFTWARE_SITE_ID)
                        else:
                            add_this_label = True
                        if (add_this_label):
                            if (not (label_to_test in test_labels)):
                                test_labels.append(label_to_test)
            print(test_labels)
            suite = super().build_suite(test_labels=test_labels, extra_tests=extra_tests, **kwargs)

            # 新增随机抽取逻辑
            if self.random_count is not None:
                # 提取所有测试用例到列表
                all_tests = []
                for test_case in suite:
                    if hasattr(test_case, '_tests'):
                        all_tests.extend(test_case._tests)
                    else:
                        all_tests.append(test_case)
                
                total = len(all_tests)
                if self.random_count >= total:
                    selected_tests = all_tests
                else:
                    # 随机抽取指定数量的用例
                    selected_tests = random.sample(all_tests, self.random_count)
                
                # 构建新的测试套件
                new_suite = TestSuite()
                for test in selected_tests:
                    new_suite.addTest(test)
                
                suite = new_suite
            
            return suite

        def setup_test_environment(self, **kwargs):
            super().setup_test_environment(**kwargs)
            django_settings.TEST_ALL_LANGUAGES = self.test_all_languages

        def teardown_test_environment(self, **kwargs):
            super().teardown_test_environment(**kwargs)
            del django_settings.TEST_ALL_LANGUAGES

使用示例

  • 运行200个随机测试用例:
    python manage.py test --random-count 200
    
  • 配合--shuffle参数使用(注:随机抽取已保证用例随机性,--shuffle在此场景下作用有限,但仍可兼容):
    python manage.py test --random-count 200 --shuffle
    

内容的提问来源于stack exchange,提问作者Uri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:34:56