从10月8日至10日在纽约市加入我们,了解关于 GraphQL 联邦和 API 平台工程的最新技巧、趋势和新闻。加入我们参加2024年纽约市的 GraphQL 峰会
文档
免费开始

数据构建器


数据构建器当前正处于 Apollo Kotlin 的 experimental 阶段。如果您对它们有反馈,请通过 GitHub issueKotlin Slack 社区联系我们。

为您的 生成模型和解析器,这些解析器可以从您的网络响应中创建这些模型的实例。但是,在某些情况下,例如在测试或其他场景中,手动使用已知值创建模型可能很有用。

这样做并不像看起来那么简单,尤其是当使用了 时。operationBased 模型需要实例化每一个 ,并为每个复合类型选择一个合适的 __typename

数据构建器通过提供与 Json 结构相匹配的构建器,使这一过程更为简便。

启用数据构建器

要启用数据构建器,将 generateDataBuilders 选项设置为 true

build.gradle[.kts]
apollo {
service("service") {
// ...
// Enable data builder generation
generateDataBuilders.set(true)
}
}

这是为您架构中的每个复合类型生成的构建器以及每个操作的帮助函数Data {}

示例用法

假设我们正在构建一个测试,该测试使用以下的模拟结果:

query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
firstName
lastName
age
ship {
model
speed
}
friends {
firstName
lastName
}
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}

以下是我们可以使用对应数据构建器对该模拟结果的用法

@Test
fun test() {
val data = HeroForEpisodeQuery.Data {
// Set values for particular fields of the query
hero = buildHuman {
firstName = "John"
age = 42
friends = listOf(
buildHuman {
firstName = "Jane"
},
buildHuman {
lastName = "Doe"
}
)
ship = buildStarship {
model = "X-Wing"
}
}
}
assertEquals("John", data.hero.firstName)
assertEquals(42, data.hero.age)
}

在这个例子中,hero字段是一个具有指定值的Human对象(firstNameage)。lastNameheight的值会自动使用模拟值填充。ship的速度、第一位朋友的姓氏和第二位朋友的名字的值也将自动填充。

您可以用buildHuman代替上面的buildDroid来创建一个Droid对象。

别名

由于数据构建器是基于架构的,并且是在您的查询中定义的,所以代码生成无法为它们生成构建器字段。相反,您需要明确指定它们。

给定一个Query,如下所示:

query GetHeroes {
luke: hero(id: "1002") {
name
}
leia: hero(id: "1003") {
name
}
}

您可以这样生成一个假数据模型

val data = GetHeroes.Data {
this["luke"] = buildHumanHero {
name = "Luke"
}
this["leia"] = buildHumanHero {
name = "Leia"
}
}

@skip@include指令

默认情况下,数据构建器与您的架构中定义的类型匹配。如果字段是非空的,您必须提供值或让默认提供值。这对于@skip@include指令是一个问题,其中字段可能是非空的,甚至是可选的。为了处理这种情况,使用与别名相同的语法,并设置值为Optional.Absent

query Skip($skip: Boolean!) {
nonNullableInt @skip(if: $skip)
}

您可以这样生成一个假数据模型

val data = SkipQuery.Data {
this["nonNullableInt"] = Optional.Absent
}
assertNull(data.nonNullableInt)

配置默认字段值

要为字段设置默认值,数据构建者使用FakeResolver接口的实现。默认情况下,它们使用DefaultFakeResolver的实例。

默认的DefaultFakeResolver为每个String字段赋予字段的名称作为默认值,并为Int字段增加计数。它为其他类型定义了类似的默认行为。

您可以创建自己的自定义 FakeResolver实现(可以选择先委托给DefaultFakeResolver以获得快速入门)。然后,像这样将实现作为参数传递给Data函数:

// A FakeResolver implementation that assigns -1 to all Int fields
class MyFakeResolver : FakeResolver {
private val delegate = DefaultFakeResolver(__Schema.all)
override fun resolveLeaf(context: FakeResolverContext): Any {
return when (context.mergedField.type.leafType().name) {
"Int" -> -1 // Always use -1 for Int
else -> delegate.resolveLeaf(context)
}
}
override fun resolveListSize(context: FakeResolverContext): Int {
// Delegate to the default behaviour
return delegate.resolveListSize(context)
}
override fun resolveMaybeNull(context: FakeResolverContext): Boolean {
// Never
return false
}
override fun resolveTypename(context: FakeResolverContext): String {
// Delegate to the default behaviour
return delegate.resolveTypename(context)
}
}
@Test
fun test() {
val data = HeroForEpisodeQuery.Data(resolver = MyFakeResolver()) {
hero = buildHuman {
firstName = "John"
}
}
// Unspecified Int field is -1
assertEquals(-1, data.hero.age)
}

使用代码片段

由于片段片段可以定义在接口和联合体中,因此您需要明确您想要建模的具体类型。例如,如果您有一个Animal接口,请使用以下方式创建一个Lion片段数据:

val data = AnimalDetailsImpl.Data(Lion) {
// you can access roar here
roar = "Grrrrr"
}

有时,您可能想测试服务端上定义的未知于客户端的新类型。为了做到这一点,请使用抽象类型(Animal在此处)作为Data构造函数的第一参数。

在这些情况下,您需要显式指定返回的对象类型的__typename:

val data = AnimalDetailsImpl.Data(Animal) {
// the client doesn't know about this type yet, and you need to specify it explicitly
__typename = "Brontaroc"
species = "alien"
}
上一个
模拟GraphQL响应
下一个
Android Studio插件
评价文章评价在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,以Apollo GraphQL名义经营。

隐私政策

公司