Analysis of 2,376 tests reveals that 24.2% (576 tests) are single-operation tests that only create, update, or delete a resource and verify the result. However, 17.3% of these (121 tests) are actually integration tests due to their complexity.
| Test Type | Count | Percentage | Classification |
|---|---|---|---|
| Single-Operation Tests | 576 | 24.2% | Unit-like |
| - CREATE only | 325 | 13.7% | Unit-like |
| - UPDATE only | 135 | 5.7% | Unit-like |
| - DELETE only | 116 | 4.9% | Unit-like |
| Multi-Operation Tests | 709 | 29.8% | Integration |
| - CREATE + UPDATE | 336 | 14.1% | Integration |
| - CREATE + DELETE | 54 | 2.3% | Integration |
| - Full CRUD | 120 | 5.0% | Integration |
| - Complex integration | 199 | 8.4% | Integration |
| Other | 1,091 | 45.9% | Mixed |
- Single-Operation: 229 tests (26.1%)
- CREATE only: 130 (14.8%)
- UPDATE only: 66 (7.5%)
- DELETE only: 33 (3.8%)
- Multi-Operation: 285 tests (32.5%)
- CREATE + UPDATE: 178 (20.3%)
- CREATE + DELETE: 19 (2.2%)
- Full CRUD: 36 (4.1%)
- Complex: 52 (5.9%)
- Single-Operation: 233 tests (25.2%)
- CREATE only: 122 (13.2%)
- UPDATE only: 50 (5.4%)
- DELETE only: 61 (6.6%)
- Multi-Operation: 206 tests (22.3%)
- CREATE + UPDATE: 94 (10.2%)
- CREATE + DELETE: 9 (1.0%)
- Full CRUD: 35 (3.8%)
- Complex: 68 (7.4%)
- Single-Operation: 114 tests (19.9%)
- CREATE only: 73 (12.7%)
- UPDATE only: 19 (3.3%)
- DELETE only: 22 (3.8%)
- Multi-Operation: 218 tests (38.0%)
- CREATE + UPDATE: 64 (11.1%)
- CREATE + DELETE: 26 (4.5%)
- Full CRUD: 49 (8.5%)
- Complex: 79 (13.8%)
Analysis of 700 single-operation tests:
| Operation | Total | Simple Unit-like | Actually Integration | % Integration |
|---|---|---|---|---|
| CREATE only | 426 | 339 (79.6%) | 87 (20.4%) | 20.4% |
| UPDATE only | 158 | 124 (78.5%) | 34 (21.5%) | 21.5% |
| DELETE only | 116 | 116 (100.0%) | 0 (0.0%) | 0.0% |
| TOTAL | 700 | 579 (82.7%) | 121 (17.3%) | 17.3% |
Key Insight: While DELETE tests are always simple, ~20% of CREATE and UPDATE tests are actually integration tests because they involve:
- Multiple entity types (>2)
- Complex operations (sync, publish, promote, register)
- Multiple dependencies/fixtures (>3)
- Cross-system interactions
def test_positive_create_unlimited_hosts(target_sat):
"""Create a plain vanilla activation key.
:expectedresults: Check that activation key is created and its
"unlimited_hosts" attribute defaults to true.
"""
assert target_sat.api.ActivationKey().create().unlimited_hosts is TrueCharacteristics:
- Single operation:
.create() - Single entity type: ActivationKey
- Single assertion
- No external dependencies
- Classification: Unit Test
def test_positive_repo_sync_publish_promote_cv(
self, module_org, module_lce, repo, target_sat
):
"""Synchronize repository with SRPMs, add repository to content view
and publish, promote content view
:expectedresults: srpms can be listed in organization, content view,
Lifecycle env
"""
repo.sync() # External operation
cv = target_sat.api.ContentView(
organization=module_org,
repository=[repo]
).create()
cv.publish() # Complex operation
cv = cv.read()
cv.version[0].promote(data={'environment_ids': module_lce.id}) # Another operation
# Multiple assertions across different systems...Characteristics:
- Multiple operations: sync, create, publish, promote
- Multiple entity types: Repository, ContentView, LifecycleEnvironment
- Multiple fixtures: module_org, module_lce, repo
- Cross-system interactions
- Classification: Integration Test (despite being labeled "create")
def test_positive_update_name(new_name, target_sat, module_org):
"""Create activation key providing the initial name, then update
its name to another valid name.
:expectedresults: Activation key is created, and its name can be updated.
"""
act_key = target_sat.api.ActivationKey(organization=module_org).create()
updated = target_sat.api.ActivationKey(
id=act_key.id,
organization=module_org,
name=new_name
).update(['name'])
assert new_name == updated.nameCharacteristics:
- Primary operation:
.update() - Setup: minimal create
- Single entity type
- Single assertion
- Classification: Unit Test (tests update functionality)
def test_positive_create_and_update_with_name(target_sat):
"""Create and update a host with different names and minimal input parameters
:expectedresults: A host is created and updated with expected name
"""
name = gen_choice(datafactory.valid_hosts_list())
host = target_sat.api.Host(name=name).create()
assert host.name == f'{name}.{host.domain.read().name}'
new_name = gen_choice(datafactory.valid_hosts_list())
host.name = new_name
host = host.update(['name'])
assert host.name == f'{new_name}.{host.domain.read().name}'Characteristics:
- Multiple operations: create, read, update
- Tests full lifecycle
- Multiple assertions
- Classification: Integration Test (tests create-update workflow)
-
They Test Real System Behavior
- Even "simple" CREATE tests interact with database, API layer, validation logic
- Tests actual HTTP requests/responses (API) or command execution (CLI)
- Involves multiple system components (controllers, models, database)
-
They Have External Dependencies
- Require running Satellite instance
- Need database access
- May require other entities (organization, location)
-
They Test More Than One Thing
- CREATE tests verify: validation, persistence, default values, relationships
- UPDATE tests verify: validation, persistence, change detection
- DELETE tests verify: cascade behavior, cleanup, constraints
-
They Test Single Operation
- Focus on one CRUD operation
- Don't test workflows or business processes
- Don't test interactions between multiple operations
-
They're Isolated
- Don't depend on complex setup
- Don't test cross-feature interactions
- Can run independently
-
They're Fast
- Single API call or CLI command
- Minimal setup/teardown
- Quick execution
Simple single-operation tests (579 tests, 82.7%):
- Classification: Functional/Component Tests
- Not pure unit tests (involve real system)
- Not integration tests (single operation, isolated)
- Best described as: "Component-level functional tests"
Complex single-operation tests (121 tests, 17.3%):
- Classification: Integration Tests
- Involve multiple systems (sync, publish, promote)
- Test cross-component workflows
- Have complex dependencies
Should NOT be considered duplicates even if multiple tests do "create":
- Each tests different attributes/scenarios
- Each tests different validation rules
- Each tests different edge cases
Example: These are NOT duplicates:
test_positive_create_with_name() # Tests name validation
test_positive_create_with_description() # Tests description validation
test_positive_create_with_lce() # Tests lifecycle environment assignmentWhen single-operation tests CAN be consolidated:
- Testing same attribute with different values → parametrize
- Testing same operation with different lookup methods → parametrize
- Testing same validation with different invalid inputs → parametrize
Example: These CAN be consolidated:
# Before: 3 separate tests
test_positive_create_with_name_alpha()
test_positive_create_with_name_numeric()
test_positive_create_with_name_special()
# After: 1 parametrized test
@pytest.mark.parametrize('name', [alpha_name, numeric_name, special_name])
test_positive_create_with_name(name)CREATE+UPDATE tests (336 tests):
- Test full lifecycle workflows
- Verify state transitions
- Test operation sequences
- Should be preserved - they provide unique coverage
Full CRUD tests (120 tests):
- Test complete resource lifecycle
- Verify cleanup and cascade
- Test end-to-end workflows
- Should be preserved - they're true E2E tests
Create clear test categories:
# Component-level functional test (single operation)
@pytest.mark.component
def test_positive_create_with_name(name):
"""Test activation key creation with valid name."""
ak = api.ActivationKey(name=name).create()
assert ak.name == name
# Integration test (multiple operations)
@pytest.mark.integration
def test_positive_create_and_update_with_name(name):
"""Test activation key creation and update workflow."""
ak = api.ActivationKey(name=name).create()
assert ak.name == name
new_name = gen_string('alpha')
ak.name = new_name
ak = ak.update(['name'])
assert ak.name == new_name
# End-to-end test (full workflow)
@pytest.mark.e2e
def test_positive_full_lifecycle():
"""Test complete activation key lifecycle."""
ak = api.ActivationKey().create()
# ... multiple operations ...
ak.delete()Do NOT remove single-operation tests just because they're "simple":
- They test important validation logic
- They're fast and reliable
- They provide clear failure signals
- They document expected behavior
Good consolidation:
# Before: 5 tests
test_create_with_name_alpha()
test_create_with_name_numeric()
test_create_with_name_utf8()
test_create_with_name_latin1()
test_create_with_name_long()
# After: 1 test
@pytest.mark.parametrize('name', [alpha, numeric, utf8, latin1, long_name])
test_create_with_name(name)Bad consolidation:
# DON'T DO THIS - loses clarity
@pytest.mark.parametrize('operation', ['create', 'update', 'delete'])
def test_activation_key_operations(operation):
# Too generic, hard to debug failuresFrom previous analysis, the real duplication is:
-
Tests with identical logic (150-200 tests)
- Same operations, same assertions
- Only difference is the attribute being tested
- These should be consolidated
-
Tests with similar patterns (300-400 tests)
- Follow same workflow
- Test same scenarios
- These could be consolidated with parametrization
| Category | Count | % of Total | Should Preserve? |
|---|---|---|---|
| Simple CREATE tests | 339 | 14.3% | ✓ Yes - test validation |
| Simple UPDATE tests | 124 | 5.2% | ✓ Yes - test updates |
| Simple DELETE tests | 116 | 4.9% | ✓ Yes - test deletion |
| Complex CREATE/UPDATE (integration) | 121 | 5.1% | ✓ Yes - integration tests |
| CREATE+UPDATE workflows | 336 | 14.1% | ✓ Yes - integration tests |
| Full CRUD workflows | 120 | 5.0% | ✓ Yes - E2E tests |
| Complex integration | 199 | 8.4% | ✓ Yes - integration tests |
| Tests with semantic duplication | 150-200 | 6-8% | ✗ Can consolidate |
From single-operation tests:
- Consolidate variations: ~50-80 tests
- Parametrize lookup methods: ~30-40 tests
- Total: 80-120 tests (3-5% reduction)
From multi-operation tests:
- Consolidate similar workflows: ~40-60 tests
- Total: 40-60 tests (2% reduction)
Combined with semantic duplication:
- Total reduction potential: 150-200 tests (6-8%)
- Tests remaining: 2,176-2,226 (maintaining full coverage)
-
576 tests (24.2%) are single-operation tests, but this is NOT a problem:
- 82.7% are legitimate component-level tests
- 17.3% are actually integration tests
- They provide valuable, focused testing
-
The real duplication is semantic, not operational:
- 150-200 tests execute identical logic with minor variations
- These can be consolidated through parametrization
- This represents 6-8% reduction potential
-
Multi-operation tests (709 tests, 29.8%) are true integration tests:
- They test workflows and state transitions
- They should be preserved
- They provide unique coverage
-
Recommendation: Focus on consolidating semantic duplicates, not reducing single-operation tests. The distinction between unit/integration matters less than whether tests provide unique value.