CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pylint

Comprehensive static code analysis tool for Python that performs deep code inspection without executing the program

Pending
Overview
Eval results
Files

test-utilities.mddocs/

Test Utilities

Testing framework for developing and validating custom checkers, including test case base classes, message validation, and functional testing support. Pylint's test utilities enable comprehensive testing of static analysis functionality and custom checker development.

Capabilities

Base Test Classes

Foundation classes for testing checkers and pylint functionality.

class CheckerTestCase:
    """
    Base class for checker unit tests.
    
    Provides utilities for testing individual checkers
    with AST nodes and message validation.
    """
    
    CHECKER_CLASS = None  # Set to checker class being tested
    
    def setup_method(self):
        """Setup test environment before each test method."""
    
    def walk(self, node):
        """
        Walk AST node with the checker.
        
        Args:
            node: AST node to walk
        """
    
    def assertNoMessages(self):
        """Assert that no messages were generated."""
    
    def assertAddsMessages(self, *messages):
        """
        Assert that specific messages were added.
        
        Args:
            *messages: Expected MessageTest instances
        """

class MessageTest:
    """
    Test representation of an expected message.
    
    Used to verify that checkers generate expected
    messages for specific code patterns.
    """
    
    def __init__(self, msg_id, node=None, line=None, args=None, 
                 confidence=None, col_offset=None, end_line=None, 
                 end_col_offset=None):
        """
        Initialize message test.
        
        Args:
            msg_id (str): Expected message ID
            node: Expected AST node (optional)
            line (int): Expected line number
            args (tuple): Expected message arguments
            confidence (str): Expected confidence level
            col_offset (int): Expected column offset
            end_line (int): Expected end line
            end_col_offset (int): Expected end column
        """

Functional Testing

Classes and utilities for functional testing of pylint behavior.

class FunctionalTestFile:
    """
    Functional test file representation.
    
    Represents a Python file used for functional testing
    with expected messages and configuration.
    """
    
    def __init__(self, directory, filename):
        """
        Initialize functional test file.
        
        Args:
            directory (str): Directory containing test file
            filename (str): Test file name
        """
    
    @property
    def expected_messages(self):
        """
        Get expected messages from test file.
        
        Returns:
            list: Expected message objects
        """
    
    @property 
    def pylintrc(self):
        """
        Get pylintrc path for test.
        
        Returns:
            str: Path to test-specific pylintrc
        """

class LintModuleTest:
    """
    Module linting test utilities.
    
    Provides utilities for testing pylint behavior
    on complete modules and packages.
    """
    
    def __init__(self, test_file):
        """
        Initialize module test.
        
        Args:
            test_file: FunctionalTestFile instance
        """
    
    def runTest(self):
        """Run the functional test."""
    
    def _check_result(self, got_messages, expected_messages):
        """
        Check test results against expectations.
        
        Args:
            got_messages (list): Actual messages from pylint
            expected_messages (list): Expected messages
        """

Test-Specific Linter

Specialized linter implementation for testing purposes.

class UnittestLinter:
    """
    Test-specific linter implementation.
    
    Simplified linter for unit testing that provides
    controlled environment and message collection.
    """
    
    def __init__(self):
        """Initialize unittest linter."""
        self.config = None
        self.reporter = None
    
    def check(self, files_or_modules):
        """
        Check files for testing.
        
        Args:
            files_or_modules: Files or modules to check
        """
    
    def add_message(self, msg_id, line=None, node=None, args=None,
                   confidence=None, col_offset=None):
        """
        Add message during testing.
        
        Args:
            msg_id (str): Message identifier
            line (int): Line number
            node: AST node
            args (tuple): Message arguments  
            confidence (str): Confidence level
            col_offset (int): Column offset
        """

Configuration Utilities

Functions for configuring tests and test environments.

def set_config(**kwargs):
    """
    Set configuration for tests.
    
    Provides a convenient way to set pylint configuration
    options during test execution.
    
    Args:
        **kwargs: Configuration options to set
        
    Example:
        set_config(max_line_length=100, disable=['missing-docstring'])
    """

def tokenize_str(code):
    """
    Tokenize Python code string.
    
    Utility function for testing token-based checkers
    by converting code strings to token streams.
    
    Args:
        code (str): Python code to tokenize
        
    Returns:
        list: Token objects
    """

Test Reporters

Specialized reporters for testing and validation.

class GenericTestReporter:
    """
    Generic test reporter.
    
    Collects messages during testing for validation
    and provides access to test results.
    """
    
    def __init__(self):
        """Initialize generic test reporter."""
        self.messages = []
    
    def handle_message(self, msg):
        """
        Handle message during testing.
        
        Args:
            msg: Message to collect
        """
        self.messages.append(msg)
    
    def finalize(self):
        """
        Finalize testing and return results.
        
        Returns:
            list: Collected messages
        """
        return self.messages

class MinimalTestReporter:
    """
    Minimal test reporter.
    
    Provides minimal message collection for
    lightweight testing scenarios.
    """
    
    def __init__(self):
        """Initialize minimal reporter."""
        self.messages = []

class FunctionalTestReporter:
    """
    Functional test reporter.
    
    Specialized reporter for functional testing
    with enhanced message formatting and comparison.
    """
    
    def __init__(self):
        """Initialize functional test reporter."""
        self.messages = []
    
    def display_reports(self, layout):
        """Display functional test reports."""
        pass

Usage Examples

Basic Checker Testing

import astroid
from pylint.testutils import CheckerTestCase, MessageTest
from my_checkers import FunctionNamingChecker

class TestFunctionNamingChecker(CheckerTestCase):
    """Test cases for function naming checker."""
    
    CHECKER_CLASS = FunctionNamingChecker
    
    def test_function_with_good_name(self):
        """Test that functions with good names pass."""
        node = astroid.extract_node('''
        def get_user_name(): #@
            return "test"
        ''')
        
        with self.assertNoMessages():
            self.walk(node)
    
    def test_function_with_bad_name(self):
        """Test that functions with bad names fail."""
        node = astroid.extract_node('''
        def userName(): #@
            return "test"
        ''')
        
        expected_message = MessageTest(
            msg_id='invalid-function-name',
            node=node,
            args=('userName',),
            line=2,
            col_offset=0
        )
        
        with self.assertAddsMessages(expected_message):
            self.walk(node)
    
    def test_function_with_config(self):
        """Test checker with custom configuration."""
        with self.assertNoMessages():
            # Set custom configuration
            self.checker.config.allowed_function_prefixes = ['create', 'build']
            
            node = astroid.extract_node('''
            def create_object(): #@
                pass
            ''')
            
            self.walk(node)

Functional Testing

from pylint.testutils import FunctionalTestFile, LintModuleTest
import os

class TestMyCheckersFunctional:
    """Functional tests for custom checkers."""
    
    def test_complex_scenarios(self):
        """Test complex scenarios with functional tests."""
        test_dir = 'tests/functional'
        test_file = 'my_checker_test.py'
        
        functional_test = FunctionalTestFile(test_dir, test_file)
        test_case = LintModuleTest(functional_test)
        test_case.runTest()
    
    def create_functional_test_file(self):
        """Create a functional test file."""
        test_content = '''
"""Test module for custom checker."""

def bad_function_name():  # [invalid-function-name]
    """This should trigger a naming violation."""
    pass

def get_value():  # No violation expected
    """This should pass naming validation.""" 
    return 42

class BadClassName:  # [invalid-class-name]
    """This should trigger a class naming violation."""
    pass
'''
        
        with open('tests/functional/my_checker_test.py', 'w') as f:
            f.write(test_content)

Custom Test Utilities

from pylint.testutils import UnittestLinter, GenericTestReporter

class CustomTestFramework:
    """Custom testing framework for specific needs."""
    
    def __init__(self):
        self.linter = UnittestLinter()
        self.reporter = GenericTestReporter()
        self.linter.set_reporter(self.reporter)
    
    def test_code_snippet(self, code, expected_messages=None):
        """
        Test a code snippet and validate results.
        
        Args:
            code (str): Python code to test
            expected_messages (list): Expected message IDs
        """
        # Create temporary file
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', 
                                       delete=False) as f:
            f.write(code)
            temp_file = f.name
        
        try:
            # Run pylint on the code
            self.linter.check([temp_file])
            messages = self.reporter.finalize()
            
            # Validate results
            if expected_messages:
                actual_msg_ids = [msg.msg_id for msg in messages]
                assert set(actual_msg_ids) == set(expected_messages), \
                    f"Expected {expected_messages}, got {actual_msg_ids}"
            
            return messages
        finally:
            # Clean up
            import os
            os.unlink(temp_file)
    
    def assert_no_violations(self, code):
        """Assert that code has no violations."""
        messages = self.test_code_snippet(code)
        assert len(messages) == 0, f"Unexpected violations: {messages}"
    
    def assert_violations(self, code, expected_msg_ids):
        """Assert that code has specific violations."""
        self.test_code_snippet(code, expected_msg_ids)

# Usage example
framework = CustomTestFramework()

# Test clean code
framework.assert_no_violations('''
def get_user_data():
    """Get user data from database."""
    return {"name": "test"}
''')

# Test code with violations
framework.assert_violations('''
def userData():  # Missing docstring, bad naming
    return {"name": "test"}
''', ['missing-docstring', 'invalid-name'])

Mock and Patch Testing

from unittest.mock import patch, MagicMock
from pylint.testutils import CheckerTestCase

class TestCheckerWithMocks(CheckerTestCase):
    """Test checker using mocks for external dependencies."""
    
    CHECKER_CLASS = MyCustomChecker
    
    @patch('mypackage.external_service.check_api')
    def test_checker_with_external_service(self, mock_api):
        """Test checker that depends on external service."""
        # Setup mock
        mock_api.return_value = True
        
        node = astroid.extract_node('''
        def process_data(): #@
            # This would normally call external service
            pass
        ''')
        
        with self.assertNoMessages():
            self.walk(node)
        
        # Verify mock was called
        mock_api.assert_called_once()
    
    def test_checker_with_mock_config(self):
        """Test checker with mocked configuration."""
        # Mock configuration
        mock_config = MagicMock()
        mock_config.strict_mode = True
        mock_config.threshold = 10
        
        self.checker.config = mock_config
        
        node = astroid.extract_node('''
        def test_function(): #@
            pass
        ''')
        
        # Test behavior changes based on config
        with self.assertAddsMessages(
            MessageTest('strict-mode-violation', node=node)
        ):
            self.walk(node)

Performance Testing

import time
from pylint.testutils import CheckerTestCase

class TestCheckerPerformance(CheckerTestCase):
    """Performance tests for checker efficiency."""
    
    CHECKER_CLASS = MyPerformanceChecker
    
    def test_checker_performance(self):
        """Test checker performance on large code."""
        # Generate large test case
        large_code = '''
def large_function():
    """Large function for performance testing."""
''' + '\n'.join([f'    var_{i} = {i}' for i in range(1000)])
        
        node = astroid.parse(large_code)
        
        # Measure execution time
        start_time = time.time()
        self.walk(node)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Assert reasonable performance
        assert execution_time < 1.0, \
            f"Checker too slow: {execution_time:.2f}s"
        
        # Verify functionality still works
        self.assertNoMessages()  # or expected messages
    
    def benchmark_checker(self, iterations=100):
        """Benchmark checker performance."""
        node = astroid.extract_node('''
        def benchmark_function(): #@
            return "test"
        ''')
        
        total_time = 0
        for _ in range(iterations):
            start = time.time()
            self.walk(node)
            total_time += time.time() - start
        
        avg_time = total_time / iterations
        print(f"Average execution time: {avg_time*1000:.2f}ms")
        
        return avg_time

Test Configuration

Test Settings

# Configure test environment
from pylint.testutils import set_config

def setup_test_config():
    """Setup common test configuration."""
    set_config(
        disable=['missing-docstring'],  # Disable for test simplicity
        max_line_length=120,            # Longer lines in tests
        good_names=['i', 'j', 'k', 'x', 'y', 'z', 'test_var'],
        reports=False,                  # Disable reports in tests
        score=False                     # Disable scoring in tests
    )

# Test-specific pylintrc
TEST_PYLINTRC = '''
[MAIN]
load-plugins=my_test_plugin

[MESSAGES CONTROL]
disable=missing-docstring,invalid-name

[BASIC]
good-names=i,j,k,x,y,z,test_var,mock_obj

[FORMAT]
max-line-length=120

[REPORTS]
reports=no
score=no
'''

Continuous Integration Testing

def run_test_suite():
    """Run complete test suite for CI/CD."""
    import unittest
    import sys
    
    # Discover and run all tests
    loader = unittest.TestLoader()
    suite = loader.discover('tests/', pattern='test_*.py')
    
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Exit with error code if tests failed
    if not result.wasSuccessful():
        sys.exit(1)
    
    print(f"All tests passed: {result.testsRun} tests")

# GitHub Actions example
'''
name: Test Custom Checkers
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, '3.10', 3.11]
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        pip install pylint pytest
        pip install -e .
    - name: Run tests
      run: python -m pytest tests/
    - name: Run functional tests
      run: python run_test_suite.py
'''

Install with Tessl CLI

npx tessl i tessl/pypi-pylint

docs

built-in-checkers.md

checker-development.md

configuration.md

core-linting.md

extensions.md

index.md

messages.md

pyreverse.md

reporters.md

test-utilities.md

tile.json