CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-nose2

A testing framework that extends unittest with plugins and enhanced discovery capabilities

Pending
Overview
Eval results
Files

configuration.mddocs/

Configuration Management

System for managing test configuration through command-line arguments, configuration files, and programmatic settings. Provides flexible configuration options for test discovery, plugin behavior, and execution parameters.

Capabilities

Configuration Class

The Config class provides type-safe access to configuration values with default handling.

class Config:
    """
    Configuration for a plugin or other entities.
    
    Encapsulates configuration for a single plugin or element.
    Corresponds to a ConfigParser section but provides extended
    interface for extracting items as specific types.
    """
    
    def __init__(self, items):
        """
        Initialize configuration with key-value pairs.
        
        Parameters:
        - items: List of (key, value) tuples from config section
        """
    
    def __getitem__(self, key):
        """
        Get raw configuration values for key.
        
        Parameters:
        - key: Configuration key name
        
        Returns:
        List of values for the key
        """
    
    def as_bool(self, key, default=None):
        """
        Get key value as boolean.
        
        1, t, true, on, yes, y (case insensitive) are accepted as True.
        All other values are False. Empty setting (key=) returns False.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        Boolean value or default
        """
    
    def as_int(self, key, default=None):
        """
        Get key value as integer.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        Integer value or default
        """
    
    def as_float(self, key, default=None):
        """
        Get key value as float.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        Float value or default
        """
    
    def as_str(self, key, default=None):
        """
        Get key value as string.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        String value or default
        """
    
    def as_list(self, key, default=None):
        """
        Get key value as list.
        
        The value is split into lines and returned as a list. Lines
        are stripped of whitespace, and lines beginning with # are
        skipped.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        List of string values or default
        """
    
    def get(self, key, default=None):
        """
        Get configuration value with default.
        
        Alias for as_str() method for compatibility.
        
        Parameters:
        - key: Configuration key name
        - default: Default value if key not found
        
        Returns:
        String value or default
        """
    
    def items(self):
        """
        Get all configuration items.
        
        Returns:
        List of (key, value) tuples
        """
    
    def has_option(self, key):
        """
        Check if configuration key exists.
        
        Parameters:
        - key: Configuration key name
        
        Returns:
        True if key exists, False otherwise
        """

Command Line Arguments

Core command-line arguments supported by nose2.

# Test Discovery Arguments
def add_discovery_arguments(parser):
    """Add test discovery arguments to parser."""
    parser.add_argument('-s', '--start-dir', 
                       help='Directory to start discovery')
    parser.add_argument('-t', '--top-level-directory',
                       help='Top level directory of project')
    parser.add_argument('-p', '--pattern',
                       help='Pattern to match test files')

# Plugin Arguments  
def add_plugin_arguments(parser):
    """Add plugin-related arguments to parser."""
    parser.add_argument('--plugin',
                       help='Load this plugin module')
    parser.add_argument('--exclude-plugin', 
                       help='Do not load this plugin module')
    parser.add_argument('--no-plugins',
                       help='Do not load any plugins')

# Configuration Arguments
def add_config_arguments(parser):
    """Add configuration file arguments to parser."""
    parser.add_argument('-c', '--config',
                       help='Config files to load')
    parser.add_argument('--no-user-config',
                       help='Do not load user config files')

# Output Arguments
def add_output_arguments(parser):
    """Add output control arguments to parser."""
    parser.add_argument('-v', '--verbose',
                       help='Increase verbosity')
    parser.add_argument('-q', '--quiet',
                       help='Decrease verbosity')
    parser.add_argument('--log-level',
                       help='Set logging level')

Configuration Files

Configuration File Locations

nose2 searches for configuration files in the following order:

  1. Files specified with --config or -c
  2. Project files: unittest.cfg, nose2.cfg, pyproject.toml (in start directory)
  3. User files: ~/.unittest.cfg, ~/.nose2.cfg (if --no-user-config not used)

Configuration File Format

# unittest.cfg or nose2.cfg
[unittest]
# Core nose2 settings
start-dir = tests
top-level-directory = .
pattern = test_*.py
test-method-prefix = test
plugins = nose2.plugins.coverage
          nose2.plugins.junitxml
          my_custom_plugin

# Plugin-specific sections
[coverage]
always-on = True
coverage = mypackage
coverage-report = html
coverage-config = .coveragerc

[junitxml]
always-on = False
path = test-results.xml

[my-custom-plugin]  
enabled = true
threshold = 1.0
output-file = results.txt

TOML Configuration (pyproject.toml)

[tool.nose2]
start-dir = "tests"
top-level-directory = "."
pattern = "test_*.py"
plugins = [
    "nose2.plugins.coverage",
    "nose2.plugins.junitxml"
]

[tool.nose2.coverage]
always-on = true
coverage = ["mypackage"]
coverage-report = "html"

[tool.nose2.junitxml]
path = "test-results.xml"

Usage Examples

Programmatic Configuration

from nose2.session import Session
from nose2.config import Config

# Create and configure session
session = Session()

# Load configuration files
session.loadConfigFiles('unittest.cfg', 'nose2.cfg')

# Get plugin configuration
coverage_config = session.get('coverage')
enabled = coverage_config.as_bool('always-on', default=False)
report_type = coverage_config.as_str('coverage-report', default='term')
packages = coverage_config.as_list('coverage', default=[])

print(f"Coverage enabled: {enabled}")
print(f"Report type: {report_type}")
print(f"Packages: {packages}")

Plugin Configuration

from nose2.events import Plugin

class MyPlugin(Plugin):
    configSection = 'my-plugin'
    
    def __init__(self):
        # Extract all config values in __init__ for sphinx docs
        self.enabled = self.config.as_bool('enabled', default=True)
        self.threshold = self.config.as_float('threshold', default=1.0)
        self.output_file = self.config.as_str('output-file', default='output.txt')
        self.patterns = self.config.as_list('patterns', default=['*.py'])
        self.debug = self.config.as_bool('debug', default=False)
        
        # Check if required config is present
        if not self.config.has_option('required-setting'):
            raise ValueError("required-setting must be specified")
    
    def some_method(self):
        if self.enabled:
            # Use configuration values
            with open(self.output_file, 'w') as f:
                f.write(f"Threshold: {self.threshold}\n")
                f.write(f"Patterns: {self.patterns}\n")

Command Line Usage

# Basic test discovery
nose2

# Custom start directory and pattern
nose2 --start-dir tests --pattern 'spec_*.py'

# Load specific plugins
nose2 --plugin nose2.plugins.coverage --plugin my_plugin

# Exclude plugins
nose2 --exclude-plugin nose2.plugins.buffer

# Custom configuration file
nose2 --config my_test_config.cfg

# Verbosity control
nose2 -v -v  # Very verbose
nose2 -q     # Quiet

# Logging level
nose2 --log-level DEBUG

# No user config
nose2 --no-user-config

# No plugins at all
nose2 --no-plugins

Complex Configuration Example

# nose2.cfg - Complete configuration example
[unittest]
# Test discovery
start-dir = tests
top-level-directory = .
pattern = test_*.py
test-method-prefix = test

# Plugin loading
plugins = nose2.plugins.coverage
          nose2.plugins.junitxml
          nose2.plugins.mp
          nose2.plugins.logcapture
          custom_plugins.timing
          custom_plugins.database

# Exclude problematic plugins
exclude-plugins = nose2.plugins.debugger

# Verbosity and logging
verbosity = 2
log-level = INFO

[coverage]
always-on = true
coverage = mypackage
           mypackage.submodule
coverage-report = html
coverage-config = .coveragerc
fail-under = 80

[junitxml]
path = test-results/junit.xml
test-properties = name value
                  version 1.0

[mp]
processes = 4
test-timeout = 30

[logcapture] 
always-on = true
clear-handlers = true
filter = -nose2
log-level = DEBUG

[timing]
enabled = true
threshold = 0.5
output-file = slow-tests.txt

[database]
url = postgresql://localhost/test_db
reset = true
fixtures = fixtures/users.json
           fixtures/products.json

Environment Variable Configuration

import os
from nose2.main import PluggableTestProgram

# Override config with environment variables
start_dir = os.environ.get('NOSE2_START_DIR', 'tests')
verbosity = int(os.environ.get('NOSE2_VERBOSITY', '1'))
plugins = os.environ.get('NOSE2_PLUGINS', '').split(',') if os.environ.get('NOSE2_PLUGINS') else []

# Run with environment-based configuration
PluggableTestProgram(
    argv=['nose2', '--start-dir', start_dir, f'--verbosity={verbosity}'] + 
         [f'--plugin={p}' for p in plugins if p.strip()]
)

Configuration Validation

from nose2.events import Plugin

class ValidatedPlugin(Plugin):
    configSection = 'validated-plugin'
    
    def __init__(self):
        # Validate required settings
        required_keys = ['api_key', 'endpoint']
        for key in required_keys:
            if not self.config.has_option(key):
                raise ValueError(f"Missing required configuration: {key}")
        
        # Extract and validate settings
        self.api_key = self.config.as_str('api_key')
        self.endpoint = self.config.as_str('endpoint')
        self.timeout = self.config.as_int('timeout', default=30)
        self.retries = self.config.as_int('retries', default=3)
        
        # Validate ranges
        if self.timeout < 1 or self.timeout > 300:
            raise ValueError("timeout must be between 1 and 300 seconds")
        
        if self.retries < 0 or self.retries > 10:
            raise ValueError("retries must be between 0 and 10")
        
        # Validate formats
        if not self.endpoint.startswith('http'):
            raise ValueError("endpoint must be a valid HTTP URL")

Install with Tessl CLI

npx tessl i tessl/pypi-nose2

docs

configuration.md

index.md

plugin-development.md

test-discovery.md

test-tools.md

tile.json