Files
bradsec-ComfyUI_StringEssen…/.planning/codebase/TESTING.md
T
2026-03-03 20:13:36 +10:00

9.3 KiB

Testing Patterns

Analysis Date: 2026-03-03

Test Framework

Runner:

  • unittest (Python standard library)
  • Config: None (uses default unittest discovery)
  • No pytest.ini, setup.cfg, or tox.ini configuration detected

Assertion Library:

  • unittest.TestCase assertions (built-in)

Run Commands:

python -m unittest test_string_nodes.py               # Run all tests
python -m unittest test_string_nodes.TestStringNodes  # Run specific test class
python -m unittest test_string_nodes.TestStringNodes.test_<name>  # Run specific test

Test File Location:

  • test_string_nodes.py in project root alongside source files (/home/mark/Code/ComfyUI_StringEssentials/test_string_nodes.py)
  • Co-located with source code (not in separate tests directory)

Test File Organization

Location:

  • Co-located pattern: test file in same directory as source modules

Naming:

  • Test file: test_string_nodes.py
  • Test class: TestStringNodes (PascalCase with Test prefix)
  • Test methods: test_<functionality> format

Structure:

test_string_nodes.py
├── TestStringNodes (class)
│   ├── setUp() - Initialize node instances
│   ├── test_strip_* - Tests for StringStripNode
│   ├── test_replace_* - Tests for StringMultiReplaceNode
│   ├── test_append_* - Tests for StringConditionalAppendNode
│   └── test_contains_any_* - Tests for StringContainsAnyNode
└── if __name__ == '__main__': unittest.main()

Test Structure

Suite Organization:

class TestStringNodes(unittest.TestCase):
    def setUp(self):
        self.strip_node = StringStripNode()
        self.replace_node = StringMultiReplaceNode()
        self.append_node = StringConditionalAppendNode()
        self.contains_any_node = StringContainsAnyNode()

Patterns:

  • Setup pattern: setUp() method instantiates all node instances as instance variables for reuse across test methods
  • No teardown: No tearDown() method (no cleanup needed)
  • Assertion pattern: Direct method calls on node instances with parameter unpacking:
    result = self.strip_node.string_strip(
        input_string=input_string,
        strings_to_remove=strings_to_remove,
        match_case=False,
        match_whole_string=True,
        preserve_punctuation=True,
        remove_extra_spaces=True
    )[0]
    self.assertEqual(result, expected_output)
    

Test Method Characteristics:

  • Each test is independent and focused on a single behavior
  • Tests use explicit parameter names (keyword arguments) for clarity
  • Return value tuple unpacking: [0] for single return, tuple unpacking for multiple returns
  • Clear arrange-act-assert flow (input setup → method call → assertion)

Test Coverage by Node

StringStripNode Tests:

  • test_strip_basic_functionality: Single string removal
  • test_strip_multiple_strings: Multiple removal patterns (multiline input)
  • test_strip_case_sensitivity: Case-insensitive vs case-sensitive modes
  • test_strip_punctuation_preserved: Punctuation preservation (Issue #6)
  • test_strip_punctuation_removed: Legacy punctuation removal behavior
  • Extra spaces handling covered in test_extra_spaces_handling

StringMultiReplaceNode Tests:

  • test_replace_basic_functionality: Single replacement pair
  • test_replace_multiple_pairs: Multiple replacement pairs from multiline input
  • test_replace_case_sensitivity: Case-insensitive vs case-sensitive modes
  • test_replace_custom_delimiter: Custom delimiter support (not just ::
  • test_replace_punctuation_preserved: Punctuation preservation (Issue #6)
  • test_replace_prompt_syntax_preserved: Bracket syntax preservation (e.g., [word1:word2:.5])
  • test_replace_with_space: Trailing space preservation in replacements (Issue #8)
  • test_replace_with_leading_space: Leading space in replacement strings (Issue #8)
  • test_replace_with_whitespace_only: Multiple spaces in replacements (Issue #8)
  • test_replace_with_tab: Tab character in replacements (Issue #8)

StringConditionalAppendNode Tests:

  • test_append_string_not_found: Appending when string not present
  • test_append_string_found: No append when string already present
  • test_append_beginning: Position flag "beginning" vs "end"
  • test_append_case_insensitive: Case-insensitive search
  • test_append_case_sensitive: Case-sensitive search
  • test_append_custom_separator: Custom separator handling
  • test_append_no_separator: Empty string separator
  • test_append_multiple_strings: Multiple check strings with mixed found/not found
  • test_append_multiple_all_found: All strings already present (no append)

StringContainsAnyNode Tests:

  • test_contains_any_found: Basic found case
  • test_contains_any_not_found: Basic not found case
  • test_contains_any_multiple_first_match: Returns first match from multiple options
  • test_contains_any_case_insensitive: Default case-insensitive behavior
  • test_contains_any_case_sensitive: Case-sensitive search
  • test_contains_any_case_sensitive_found: Case-sensitive with matching case
  • test_contains_any_empty_input: Empty input string handling
  • test_contains_any_empty_substrings: Empty substrings input
  • test_contains_any_whitespace_substrings: Whitespace-only lines are ignored
  • test_contains_any_partial_match: Substring matching (anime matches animation)

Mocking

Framework: None - unittest provides basic mocking but not used in this codebase

Patterns:

  • No mock objects used
  • No external dependencies mocked
  • Tests use real node instances with direct method calls
  • All test data is embedded in test methods (no external fixtures)

What to Mock:

  • No mocking required; all node classes are stateless and dependency-free

What NOT to Mock:

  • Don't mock the node classes - test them directly with real instances

Fixtures and Factories

Test Data:

  • All test data embedded directly in test methods
  • No separate fixture files or factory classes
  • Input strings created inline:
    input_string = "This is a test string with test word"
    strings_to_remove = "test"
    

Location:

  • Test data is co-located with test methods (no separate fixtures directory)
  • Issue-specific test data clearly labeled with issue references:
    def test_strip_punctuation_preserved(self):
        # Test Issue #6 fix: punctuation should be preserved
        input_string = "Let's meet, Grandma!"
    

Coverage

Requirements: None enforced (no coverage configuration detected)

View Coverage:

# Using unittest's built-in capabilities (limited):
python -m unittest discover -v

# Better coverage with coverage.py (not currently installed):
pip install coverage
coverage run -m unittest test_string_nodes.py
coverage report

Observed Coverage:

  • StringStripNode: 100% - All major code paths tested (basic, multiple, case sensitivity, punctuation handling, spaces)
  • StringMultiReplaceNode: 100% - All code paths tested (basic, multiple, case sensitivity, custom delimiter, punctuation, whitespace preservation)
  • StringConditionalAppendNode: 100% - All code paths tested (found/not found, position, case sensitivity, separator options, multiple strings)
  • StringContainsAnyNode: 100% - All code paths tested (found/not found, case sensitivity, empty inputs, whitespace handling, partial matching)
  • StringPreviewNode: 0% - No tests (trivial pass-through logic)
  • StringTextboxNode: 0% - No tests (trivial pass-through logic)

Test Types

Unit Tests:

  • Scope: Individual node class methods
  • Approach: Direct method invocation with arranged inputs, assert on single output
  • Example: test_strip_basic_functionality tests string_strip() method with specific parameters
  • No test doubles or stubs - real objects used

Integration Tests:

  • Not present in codebase (no multi-component interaction testing)

E2E Tests:

  • Not present (ComfyUI-specific E2E would require full ComfyUI runtime)

Issue-Driven Tests:

  • Tests explicitly tied to GitHub issues in comments
  • Issue #6: Punctuation preservation tests
  • Issue #8: Whitespace preservation in replacements tests
  • Issue #9: StringContainsAnyNode feature tests
  • Pattern: When fixing a bug, issue number added as comment and test created to verify fix

Common Patterns

Async Testing:

  • Not applicable - Python standard library code without async/await

Error Testing:

  • No explicit error/exception testing
  • Edge cases tested: empty strings, whitespace-only input, multiple spaces
  • Validation through return values rather than exception handling

Testing Multiline Input:

strings_to_remove = """test
More Text"""
result = self.strip_node.string_strip(
    input_string=input_string,
    strings_to_remove=strings_to_remove,
    match_case=False,
    match_whole_string=True,
    preserve_punctuation=True,
    remove_extra_spaces=True
)[0]

Testing Multiple Parameters:

result, was_appended = self.append_node.conditional_append(
    input_string=input_string,
    strings_to_check=strings_to_check,
    position="end",
    match_case=False,
    separator=", "
)
self.assertEqual(result, expected_string)
self.assertTrue(was_appended)

Testing Boolean Returns:

  • StringConditionalAppendNode returns (string, boolean) tuple
  • StringContainsAnyNode returns (boolean, string) tuple
  • Tests unpack both return values and assert on each

Testing analysis: 2026-03-03