# FluentAssert
**Repository Path**: AhooWang/FluentAssert
## Basic Information
- **Project Name**: FluentAssert
- **Description**: Kotlin Fluent Assert
- **Primary Language**: Kotlin
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: https://github.com/Ahoo-Wang/FluentAssert
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2025-04-17
- **Last Updated**: 2026-02-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: assert, assertj, fluent-assertions, Kotlin, assertions
## README
# FluentAssert
[](https://github.com/Ahoo-Wang/FluentAssert/blob/main/LICENSE)
[](https://github.com/Ahoo-Wang/FluentAssert/releases)
[](https://central.sonatype.com/artifact/me.ahoo.test/fluent-assert-core)
[](https://app.codacy.com/gh/Ahoo-Wang/FluentAssert/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://codecov.io/gh/Ahoo-Wang/FluentAssert)
[](https://github.com/Ahoo-Wang/FluentAssert)
[](https://deepwiki.com/Ahoo-Wang/FluentAssert)
FluentAssert是一个为JDK类型提供流畅断言的Kotlin库,使您的测试更易读和富有表现力。该库使用Kotlin扩展函数包装AssertJ断言以获得更好的语法。
## 特性
- **流畅API**:以可读、自然的方式链式断言
- **空安全**:所有扩展函数都适当地处理可空类型
- **全面覆盖**:支持所有主要的JDK类型和集合
- **类型安全**:完整的Kotlin类型系统集成
- **AssertJ驱动**:利用强大的AssertJ断言库
## 安装
### 要求
- **Java**:17 或更高版本
- **Kotlin**:1.8.0 或更高版本
- **JUnit**:5.x(用于测试)
### Maven
```xml
me.ahoo.test
fluent-assert-core
0.2.2
test
```
### Gradle (Kotlin DSL)
```kotlin
testImplementation("me.ahoo.test:fluent-assert-core:0.2.2")
```
### Gradle (Groovy DSL)
```gradle
testImplementation 'me.ahoo.test:fluent-assert-core:0.2.2'
```
## 快速开始
只需在任何JDK类型上调用`.assert()`即可开始构建流畅断言:
```kotlin
import me.ahoo.test.asserts.assert
// 基本断言
val name = "FluentAssert"
name.assert().startsWith("Fluent").endsWith("Assert")
val age = 25
age.assert().isGreaterThan(18).isLessThan(100)
val isActive = true
isActive.assert().isTrue()
```
### **llms.txt** 面向AI助手
本项目包含对LLM友好的文档文件:
- **[`llms.txt`](llms.txt)**:面向AI助手的简洁项目概述
- **[`llms-full.txt`](llms-full.txt)**:完整的API参考和技术细节
- **[`AGENTS.md`](AGENTS.md)**:面向编码代理的开发指南
在贡献代码时,请使用这些文件来了解项目结构、API模式和编码标准。
## API参考
### 核心扩展函数
所有扩展函数都遵循`Type.assert(): AssertJTypeAssert`模式,其中:
- `Type`是任何支持的JDK类型(可空或不可空)
- `AssertJTypeAssert`是相应的AssertJ断言类
### 支持的类型
| 类别 | 类型 |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **基本类型** | `Boolean`, `Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `BigDecimal` |
| **文本** | `String` |
| **集合** | `Iterable`, `Iterator`, `Collection`, `Array`, `List`, `Map`, `Optional`, `Stream` |
| **时间/日期** | `Date`, `ZonedDateTime`, `LocalDateTime`, `OffsetDateTime`, `OffsetTime`, `LocalTime`, `LocalDate`, `YearMonth`, `Instant`, `Duration`, `Period`, `Temporal` |
| **I/O** | `Path`, `File`, `URL`, `URI` |
| **并发** | `Future`, `CompletableFuture`, `CompletionStage` |
| **函数式** | `Predicate` |
| **异常** | `Throwable` |
### 异常测试函数
- `assertThrownBy(shouldRaiseThrowable: () -> Unit): ThrowableAssert` - 断言代码抛出特定类型的异常
- `Throwable.assert(): ThrowableAssert` - 为异常实例创建断言
## 完整API参考
### 核心JDK类型
#### 基本类型
##### `Boolean?.assert(): BooleanAssert`
为布尔值创建断言。
```kotlin
true.assert().isTrue()
false.assert().isFalse()
val nullableBool: Boolean? = null
nullableBool.assert().isNull()
```
##### `Byte?.assert(): ByteAssert`
为字节值创建断言。
```kotlin
val value: Byte = 42
value.assert().isEqualTo(42).isPositive()
```
##### `Short?.assert(): ShortAssert`
为短整型值创建断言。
```kotlin
val value: Short = 1000
value.assert().isEqualTo(1000).isGreaterThan(0)
```
##### `Int?.assert(): IntegerAssert`
为整型值创建断言。
```kotlin
val age = 25
age.assert().isEqualTo(25).isBetween(18, 65)
```
##### `Long?.assert(): LongAssert`
为长整型值创建断言。
```kotlin
val timestamp = System.currentTimeMillis()
timestamp.assert().isPositive().isGreaterThan(0)
```
##### `Float?.assert(): FloatAssert`
为浮点数值创建断言。
```kotlin
val pi = 3.14f
pi.assert().isEqualTo(3.14f).isPositive()
```
##### `Double?.assert(): DoubleAssert`
为双精度浮点数值创建断言。
```kotlin
val price = 19.99
price.assert().isEqualTo(19.99).isPositive()
```
##### `BigDecimal?.assert(): BigDecimalAssert`
为BigDecimal值创建断言。
```kotlin
val amount = BigDecimal("123.45")
amount.assert().isEqualTo("123.45").isPositive()
```
#### 文本类型
##### `String?.assert(): StringAssert`
为字符串值创建断言。
```kotlin
val name = "FluentAssert"
name.assert()
.startsWith("Fluent")
.endsWith("Assert")
.contains("uentAss")
.hasLength(11)
```
#### 泛型类型
##### ` T?.assert(): ObjectAssert`
为任何对象类型创建断言。
```kotlin
val person = Person("John", 30)
person.assert()
.isNotNull()
.hasFieldOrPropertyWithValue("name", "John")
```
##### `?> T.assert(): GenericComparableAssert`
为可比较对象创建断言。
```kotlin
val version = "2.0.0"
version.assert()
.isGreaterThan("1.0.0")
.isLessThan("3.0.0")
```
### 集合
##### ` Iterable?.assert(): IterableAssert`
为可迭代集合创建断言。
```kotlin
val numbers = listOf(1, 2, 3, 4, 5)
numbers.assert()
.hasSize(5)
.contains(3)
.doesNotContain(6)
.allMatch { it > 0 }
```
##### ` Iterator?.assert(): IteratorAssert`
为迭代器创建断言。
```kotlin
val iterator = listOf(1, 2, 3).iterator()
iterator.assert().hasNext()
```
##### ` Collection?.assert(): CollectionAssert`
为集合创建断言。
```kotlin
val set = setOf("apple", "banana", "orange")
set.assert()
.hasSize(3)
.contains("apple")
.doesNotContain("grape")
```
##### ` Array?.assert(): ObjectArrayAssert`
为数组创建断言。
```kotlin
val array = arrayOf("a", "b", "c")
array.assert()
.hasSize(3)
.contains("b")
.doesNotContain("d")
```
##### ` List??.assert(): ListAssert`
为列表创建断言。
```kotlin
val items = listOf("apple", "banana", "orange")
items.assert()
.hasSize(3)
.contains("apple", "banana")
.element(0).isEqualTo("apple")
```
##### ` Optional?.assert(): OptionalAssert`
为Optional值创建断言。
```kotlin
val present = Optional.of("value")
present.assert()
.isPresent()
.contains("value")
val empty = Optional.empty()
empty.assert().isEmpty()
```
##### ` Map?.assert(): MapAssert`
为映射创建断言。
```kotlin
val map = mapOf("key1" to "value1", "key2" to "value2")
map.assert()
.hasSize(2)
.containsKey("key1")
.containsValue("value1")
.containsEntry("key1", "value1")
```
##### ` Stream?.assert(): ListAssert`
为流创建断言(转换为列表)。
```kotlin
val stream = listOf(1, 2, 3, 4, 5).stream()
stream.assert()
.hasSize(5)
.contains(3)
.allMatch { it > 0 }
```
### 时间和日期
##### `Date?.assert(): DateAssert`
为Date对象创建断言。
```kotlin
val date = Date()
date.assert()
.isToday()
.isBefore(Date(System.currentTimeMillis() + 1000))
```
##### `ZonedDateTime?.assert(): ZonedDateTimeAssert`
为ZonedDateTime对象创建断言。
```kotlin
val zonedDateTime = ZonedDateTime.now()
zonedDateTime.assert()
.isToday()
.hasZone(ZoneId.systemDefault())
```
##### `Temporal?.assert(): TemporalAssert`
为任何Temporal对象创建断言。
```kotlin
val instant = Instant.now()
instant.assert()
.isBefore(Instant.now().plusSeconds(1))
```
##### `LocalDateTime?.assert(): LocalDateTimeAssert`
为LocalDateTime对象创建断言。
```kotlin
val dateTime = LocalDateTime.now()
dateTime.assert()
.isToday()
.isBefore(LocalDateTime.now().plusHours(1))
```
##### `OffsetDateTime?.assert(): OffsetDateTimeAssert`
为OffsetDateTime对象创建断言。
```kotlin
val offsetDateTime = OffsetDateTime.now()
offsetDateTime.assert()
.isToday()
.hasOffset(ZoneOffset.UTC)
```
##### `OffsetTime?.assert(): OffsetTimeAssert`
为OffsetTime对象创建断言。
```kotlin
val offsetTime = OffsetTime.now()
offsetTime.assert()
.isBefore(OffsetTime.now().plusHours(1))
```
##### `LocalTime?.assert(): LocalTimeAssert`
为LocalTime对象创建断言。
```kotlin
val time = LocalTime.of(10, 30)
time.assert()
.isBefore(LocalTime.of(12, 0))
.hasHour(10)
.hasMinute(30)
```
##### `LocalDate?.assert(): LocalDateAssert`
为LocalDate对象创建断言。
```kotlin
val date = LocalDate.of(2023, 12, 25)
date.assert()
.hasYear(2023)
.hasMonth(12)
.hasDayOfMonth(25)
```
##### `YearMonth?.assert(): YearMonthAssert`
为YearMonth对象创建断言。
```kotlin
val yearMonth = YearMonth.of(2023, 12)
yearMonth.assert()
.hasYear(2023)
.hasMonth(12)
```
##### `Instant?.assert(): InstantAssert`
为Instant对象创建断言。
```kotlin
val instant = Instant.now()
instant.assert()
.isBefore(Instant.now().plusSeconds(1))
```
##### `Duration?.assert(): DurationAssert`
为Duration对象创建断言。
```kotlin
val duration = Duration.ofHours(2)
duration.assert()
.hasHours(2)
.isGreaterThan(Duration.ofHours(1))
```
##### `Period?.assert(): PeriodAssert`
为Period对象创建断言。
```kotlin
val period = Period.of(1, 2, 3)
period.assert()
.hasYears(1)
.hasMonths(2)
.hasDays(3)
```
### 文件系统和I/O
##### `Path?.assert(): PathAssert`
为Path对象创建断言。
```kotlin
val path = Paths.get("/tmp/test.txt")
path.assert()
.exists()
.isReadable()
.isRegularFile()
```
##### `File?.assert(): FileAssert`
为File对象创建断言。
```kotlin
val file = File("/tmp/test.txt")
file.assert()
.exists()
.isFile()
.canRead()
.hasName("test.txt")
```
##### `URL?.assert(): UrlAssert`
为URL对象创建断言。
```kotlin
val url = URL("https://example.com")
url.assert()
.hasHost("example.com")
.hasProtocol("https")
.hasPort(443)
```
##### `URI?.assert(): UriAssert`
为URI对象创建断言。
```kotlin
val uri = URI("https://example.com/path?query=value")
uri.assert()
.hasHost("example.com")
.hasPath("/path")
.hasQuery("query=value")
```
### 并发编程
##### ` Future?.assert(): FutureAssert`
为Future对象创建断言。
```kotlin
val future = executor.submit(Callable { "result" })
future.assert()
.isDone()
.isNotCancelled()
```
##### ` CompletableFuture?.assert(): CompletableFutureAssert`
为CompletableFuture对象创建断言。
```kotlin
val future = CompletableFuture.completedFuture("success")
future.assert()
.isCompleted()
.isCompletedWithValue("success")
```
##### ` CompletionStage?.assert(): CompletionStageAssert`
为CompletionStage对象创建断言。
```kotlin
val stage = CompletableFuture.completedFuture("result")
stage.assert()
.isCompleted()
.isCompletedWithValue("result")
```
### 函数式编程
##### ` Predicate?.assert(): PredicateAssert`
为Predicate函数创建断言。
```kotlin
val isEven = Predicate { it % 2 == 0 }
isEven.assert()
.accepts(2, 4, 6)
.rejects(1, 3, 5)
```
### 异常测试
##### ` T?.assert(): ThrowableAssert`
为Throwable对象创建断言。
```kotlin
val exception = RuntimeException("test error")
exception.assert()
.hasMessage("test error")
.isInstanceOf(RuntimeException::class.java)
```
##### `assertThrownBy(Class, () -> Unit): ThrowableAssert`
断言代码抛出特定类型的异常。
```kotlin
assertThrownBy(IllegalArgumentException::class.java) {
throw IllegalArgumentException("invalid argument")
}.assert().hasMessage("invalid argument")
```
##### `assertThrownBy(() -> Unit): ThrowableAssert` (reified)
断言代码抛出特定类型的异常(Kotlin reified版本)。
```kotlin
assertThrownBy {
throw IllegalArgumentException("invalid argument")
}.assert().hasMessage("invalid argument")
```
## 高级示例
### 复杂业务逻辑验证
```kotlin
// 用户注册验证
data class User(val name: String, val age: Int, val email: String)
fun validateUser(user: User) {
user.name.assert()
.isNotBlank()
.hasSizeBetween(2, 50)
.matches("[a-zA-Z\\s]+")
user.age.assert()
.isBetween(13, 120)
user.email.assert()
.isNotBlank()
.matches("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}")
}
// 使用
val user = User("John Doe", 25, "john@example.com")
validateUser(user)
```
### 数据处理管道测试
```kotlin
fun processData(input: List): List {
return input
.filter { it.isNotBlank() }
.map { it.uppercase() }
.distinct()
.sorted()
}
fun testDataProcessing() {
val input = listOf(" apple", "", "banana", " APPLE", "cherry")
val result = processData(input)
result.assert()
.hasSize(3)
.contains("APPLE", "BANANA", "CHERRY")
.isSorted()
.allMatch { it == it.uppercase() }
}
```
### 并发操作测试
```kotlin
suspend fun testAsyncOperations() {
val results = coroutineScope {
val deferred1 = async { fetchUser(1) }
val deferred2 = async { fetchUser(2) }
listOf(deferred1, deferred2).awaitAll()
}
results.assert()
.hasSize(2)
.allMatch { it != null }
.anySatisfy { user ->
user.name.assert().isEqualTo("John Doe")
user.id.assert().isPositive()
}
}
```
### 配置验证
```kotlin
data class DatabaseConfig(
val host: String,
val port: Int,
val database: String,
val credentials: Map
)
fun validateConfig(config: DatabaseConfig) {
config.host.assert()
.isNotBlank()
.matches("^[a-zA-Z0-9.-]+$")
config.port.assert()
.isBetween(1024, 65535)
config.database.assert()
.isNotBlank()
.hasSizeBetween(1, 64)
config.credentials.assert()
.isNotEmpty()
.containsKey("username")
.containsKey("password")
.allSatisfy { (key, value) ->
key.assert().isNotBlank()
value.assert().isNotBlank()
}
}
```
## 贡献
我们欢迎贡献!请查看我们的[贡献指南](CONTRIBUTING.md)了解详情。
### 开发环境设置
1. **克隆仓库**
```bash
git clone https://github.com/Ahoo-Wang/FluentAssert.git
cd FluentAssert
```
2. **构建项目**
```bash
./gradlew build
```
3. **运行测试**
```bash
./gradlew test
```
4. **运行代码检查**
```bash
./gradlew detekt
```
### 代码风格
- 遵循Kotlin官方编码约定
- 使用300字符最大行长度
- 为所有公共API编写全面的测试
- 使用带反引号的描述性测试方法名称
### 拉取请求流程
1. Fork仓库
2. 创建功能分支(`git checkout -b feature/amazing-feature`)
3. 提交更改(`git commit -m 'Add amazing feature'`)
4. 推送到分支(`git push origin feature/amazing-feature`)
5. 开启拉取请求
## 常见问题解答
### 一般问题
**问:什么是FluentAssert?**
答:FluentAssert是一个为JDK类型提供流畅断言的Kotlin库,通过使用Kotlin扩展函数包装AssertJ断言,使您的单元测试更易读和富有表现力。
**问:为什么应该使用FluentAssert而不是直接使用AssertJ?**
答:FluentAssert提供了更符合Kotlin习惯的API,具有更好的空安全、类型推断和IDE支持。`.assert()`语法比AssertJ的`assertThat()`更流畅和可读。
**问:FluentAssert是否已准备好用于生产环境?**
答:是的,FluentAssert稳定且可用于生产环境。它具有全面的测试覆盖率并遵循语义化版本控制。
### 技术问题
**问:FluentAssert会增加运行时开销吗?**
答:最小的。扩展函数在可能的地方被内联,该库只是委托给高度优化的AssertJ。
**问:可以将FluentAssert与其他测试框架一起使用吗?**
答:是的,FluentAssert适用于任何支持AssertJ断言的测试框架,包括JUnit 5、TestNG和Spock。
**问:空值处理是如何工作的?**
答:所有扩展函数都接受可空类型并适当地处理空值,而无需额外的样板代码。
**问:支持哪些JDK版本?**
答:FluentAssert支持Java 17及更高版本,与所有现代JDK类型和API完全兼容。
### 使用问题
**问:如何从AssertJ迁移到FluentAssert?**
答:将`assertThat(value)`替换为`value.assert()`。断言方法保持不变。
**问:可以在同一个测试中混合使用FluentAssert和AssertJ吗?**
答:是的,它们完全兼容。您可以在同一代码库中使用两种API。
**问:与AssertJ相比有什么限制吗?**
答:FluentAssert提供对所有AssertJ功能的访问。某些高级AssertJ功能可能需要直接使用AssertJ,但这种情况很少见。
### 故障排除
**问:我遇到编译错误。应该检查什么?**
答:确保您使用的是Java 17+、Kotlin 1.8.0+,并且具有正确的依赖项。检查您的IDE是否使用了正确的JDK。
**问:测试因空指针异常而失败。**
答:这通常意味着您在空值上调用方法。使用安全调用(`?.`)或在断言之前检查空值。
**问:IDE不识别扩展函数。**
答:确保FluentAssert依赖项已正确配置,并且您的IDE具有更新的Kotlin插件。
### 贡献
**问:如何贡献新的断言类型?**
答:请查看我们的[贡献指南](CONTRIBUTING.md)以获取添加新的JDK类型支持的详细说明。
**问:可以建议新功能吗?**
答:当然可以!在GitHub上使用"enhancement"标签创建一个issue来讨论新功能。
**问:发现了一个bug。如何报告?**
答:在GitHub上创建一个issue,包含详细的重现步骤、预期与实际行为,以及您的环境详细信息。
## 许可证
FluentAssert采用[Apache License 2.0](LICENSE)许可证。