Skip to content

测试 ​

CoSec 使用基于 JUnit 5、MockK、FluentAssert、Hamcrest 和 Reactor Test 的综合测试技术栈。测试遵循清晰的 Given-When-Expect 模式,并利用类型安全的流式断言。

测试技术栈 ​

库用途
JUnit 5测试框架(@Test、@ParameterizedTest)
MockKKotlin 原生 Mock(mockk、every)
FluentAssert流式断言(me.ahoo.test.asserts.assert)
Hamcrest匹配器断言(assertThat、instanceOf、equalTo)
Reactor Test响应式步骤验证(.test()、expectNext、verifyComplete)

测试策略评估 ​

DefaultPolicyEvaluatorTest ​

测试策略可以无错误地评估。使用 EvaluateRequest 测试夹具模拟请求。

mermaid
sequenceDiagram
    autonumber
    participant Test as Test Method
    participant Eval as DefaultPolicyEvaluator
    participant Policy as PolicyData
    participant Stmt as Statement

    Test->>Test: Given - Create PolicyData with statements
    Test->>Eval: evaluate(policy)
    Eval->>Policy: Check condition
    Policy-->>Eval: condition.match() = true
    Eval->>Stmt: verify(request, securityContext)
    Note over Eval,Stmt: Validates statement structure<br>and action matcher
    Stmt-->>Eval: VerifyResult
    Eval-->>Test: No exception = pass

关键测试模式:

kotlin
@Test
fun evaluateDefaultRequest() {
    var evaluateRequest: Request = EvaluateRequest()
    evaluateRequest.method.assert().isEqualTo("POST")
    evaluateRequest.remoteIp.assert().isEqualTo("127.0.0.1")
    evaluateRequest.attributes.assert().isEmpty()
}

@Test
fun safeEvaluateSwallowsTooManyRequestsException() {
    var executed = false
    DefaultPolicyEvaluator.safeEvaluate {
        executed = true
        throw TooManyRequestsException()
    }
    assertThat(executed, equalTo(true))
}

测试授权 ​

SimpleAuthorizationTest ​

使用 Mock 依赖测试完整的授权流程。使用 reactor.kotlin.test.test() 进行响应式验证。

mermaid
graph TD
    subgraph "Given"
        A["Mock PolicyRepository"]
        B["Mock AppRolePermissionRepository"]
        C["Mock Request"]
        D["SecurityContext<br>(with principal)"]
    end
    subgraph "When"
        E["authorization.authorize(request, context)"]
    end
    subgraph "Expect"
        F[".test()"]
        G[".expectNext(AuthorizeResult)"]
        H[".verifyComplete()"]
    end
    A --> E
    B --> E
    C --> E
    D --> E
    E --> F
    F --> G
    G --> H

    style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

Root 用户测试 ​

kotlin
@Test
fun authorizeWhenPrincipalIsRoot() {
    val authorization = SimpleAuthorization(policyRepository, permissionRepository)
    val securityContext = mockk<SecurityContext> {
        every { principal.id } returns CoSecPrincipal.ROOT_ID
    }
    authorization.authorize(request, securityContext)
        .test()
        .expectNext(AuthorizeResult.ALLOW)
        .verifyComplete()
}

空策略测试 ​

kotlin
@Test
fun authorizeWhenPolicyIsEmpty() {
    val policyRepository = mockk<PolicyRepository> {
        every { getGlobalPolicy() } returns Mono.empty()
        every { getPolicies(any()) } returns Mono.empty()
    }
    val authorization = SimpleAuthorization(policyRepository, permissionRepository)
    authorization.authorize(request, SimpleSecurityContext.anonymous())
        .test()
        .expectNext(AuthorizeResult.IMPLICIT_DENY)
        .verifyComplete()
}

全局策略允许测试 ​

kotlin
@Test
fun authorizeWhenGlobalPolicyIsAllowAll() {
    val globalPolicy = mockk<Policy> {
        every { id } returns "globalPolicy"
        every { condition } returns AllConditionMatcher.INSTANCE
        every { statements } returns listOf(
            StatementData(effect = Effect.ALLOW, action = AllActionMatcher.INSTANCE)
        )
    }
    val policyRepository = mockk<PolicyRepository> {
        every { getGlobalPolicy() } returns Mono.just(listOf(globalPolicy))
        every { getPolicies(any()) } returns Mono.empty()
    }
    authorization.authorize(request, SimpleSecurityContext.anonymous())
        .test()
        .expectNext(AuthorizeResult.ALLOW)
        .verifyComplete()
}

使用 JWT 测试夹具 ​

JwtFixture 对象为基于 JWT 的测试提供共享的测试算法:

kotlin
object JwtFixture {
    const val TEST_JWT_SECRET = "jwt-fixture-test-secret"
    var ALGORITHM = Algorithm.HMAC256(TEST_JWT_SECRET)
}

此夹具在 JWT 令牌转换器和验证器测试中使用,以确保一致的签名行为。

Reactor 测试模式 ​

所有响应式测试遵循 .test().expectNext().verifyComplete() 模式:

mermaid
flowchart LR
    A["Mono of AuthorizeResult"] --> B[".test()"]
    B --> C[".expectNext(expected)"]
    C --> D[".verifyComplete()"]

    style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

运行测试 ​

bash
# 运行所有测试
./gradlew test

# 运行单个模块的测试
./gradlew :cosec-core:test
./gradlew :cosec-api:test

# 运行单个测试类
./gradlew :cosec-core:test --tests "me.ahoo.cosec.policy.DefaultPolicyEvaluatorTest"

# 运行单个测试方法
./gradlew :cosec-core:test --tests "me.ahoo.cosec.authorization.SimpleAuthorizationTest.authorizeWhenPrincipalIsRoot"

# 生成代码覆盖率报告
./gradlew :code-coverage-report:codeCoverageReport

参考资料 ​

相关页面 ​

基于 Apache License 2.0 发布