Comprehensive testing standards including unit test isolation, coverage thresholds, test tagging, and TDD requirements...
This rule enforces comprehensive testing practices including unit test isolation, coverage thresholds, test tagging, and quality gates to ensure reliable, maintainable software.
Each business class or function MUST include deterministic unit tests covering at least 80% of its logic branches. The goal is to validate correctness at the unit level, independently from external systems.
To catch regressions early, improve maintainability, and ensure every class's logic behaves as expected under varied inputs. This contributes to secure, reliable software that aligns with SDLC testing practices and auditability.
unit, integration, performance, security) for selective CI runs| Metric | Target Value | Measurement Method | Enforcement Level |
|---|---|---|---|
| Module test coverage | ≥ 80% | Jacoco (Java), coverlet (.NET) | MUST |
| Coverage floor | ≥ 70% | CI test gate | MUST |
| Coverage regression | 0% drop on change | CI test gate | MUST |
| External API mock coverage | 100% of usage | Code review, mocking libs | MUST |
| Flaky test rate | ≤ 3% | CI test results tracking | MUST |
| Test tagging compliance | 100% | Test metadata review | MUST |
Java (Maven/Gradle):
<!-- Maven Surefire -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</plugin>
.NET (coverlet):
dotnet test --collect:"XPlat Code Coverage" /p:CoverageThreshold=80
Python (pytest-cov):
pytest --cov=myapp --cov-fail-under=80
Format: MethodName_Scenario_ExpectedResult
Examples:
GetUserAsync_ValidId_ReturnsUserSaveAsync_NullDto_ThrowsArgumentNullExceptionCalculate_NegativeAmount_ThrowsArgumentExceptionpublic class TaxCalculatorTests
{
private readonly IRatesApi _ratesApi = A.Fake<IRatesApi>();
private readonly TaxCalculator _calculator;
public TaxCalculatorTests()
{
_calculator = new TaxCalculator(_ratesApi);
}
[Fact]
public void Calculate_ValidAmount_ReturnsTax()
{
// Arrange
A.CallTo(() => _ratesApi.GetRate("CA")).Returns(0.12m);
// Act
var result = _calculator.Calculate(100.0m, "CA");
// Assert
Assert.Equal(12.0m, result);
}
[Theory]
[InlineData(0)]
[InlineData(-100)]
public void Calculate_InvalidAmount_ThrowsArgumentException(decimal amount)
{
// Act & Assert
Assert.Throws<ArgumentException>(() => _calculator.Calculate(amount, "CA"));
}
}
@Test
public void shouldCalculateTax() {
// Arrange
IRatesApi ratesApiMock = mock(IRatesApi.class);
when(ratesApiMock.getRate("CA")).thenReturn(0.12);
TaxCalculator calc = new TaxCalculator(ratesApiMock);
// Act
double result = calc.calculate(100.0, "CA");
// Assert
assertEquals(12.0, result, 0.01);
}
C#/.NET (xUnit):
[Trait("Category", "unit")]
[Fact]
public void GetUser_ValidId_ReturnsUser() { }
[Trait("Category", "integration")]
[Fact]
public void SaveUser_ValidUser_PersistsToDatabase() { }
Java (JUnit 5):
@Tag("unit")
@Test
public void shouldReturnUser() { }
@Tag("integration")
@Test
public void shouldPersistToDatabase() { }
public class TestDataBuilder
{
public static User CreateTestUser(string id = null)
{
return new User
{
Id = id ?? Guid.NewGuid().ToString(),
Name = $"Test User {Random.Shared.Next(1000)}",
Email = $"test{Random.Shared.Next(1000)}@example.com",
CreatedAt = DateTime.UtcNow
};
}
}
[Fact]
public void ProcessUser_ValidUser_Success()
{
// Arrange
var user = TestDataBuilder.CreateTestUser();
// Act & Assert
// ...
}
All tests MUST follow the Arrange-Act-Assert pattern:
[Fact]
public void MethodName_Scenario_ExpectedResult()
{
// Arrange - Set up test data and dependencies
var dependency = A.Fake<IDependency>();
var sut = new SystemUnderTest(dependency);
// Act - Execute the method being tested
var result = sut.MethodUnderTest(input);
// Assert - Verify the expected outcome
Assert.Equal(expectedValue, result);
}
{ClassUnderTest}Tests.cs or {ClassUnderTest}Test.javaA test is considered flaky if it:
[Trait("Category", "quarantined")] or @Tag("quarantined")All PRs MUST pass these gates: