Skip to content

测试

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

测试技术栈

用途
JUnit 5测试框架(@Test@ParameterizedTest
MockKKotlin 原生 Mock(mockkevery
FluentAssert流式断言(me.ahoo.test.asserts.assert
Hamcrest匹配器断言(assertThatinstanceOfequalTo
Reactor Test响应式步骤验证(.test()expectNextverifyComplete

测试策略评估

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 {
    var ALGORITHM = Algorithm.HMAC256("FyN0Igd80Gas8stTavArGKOYnS9uLWGA_")
}

此夹具在 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 发布