Docs
Launch GraphOS Studio

Data builders (experimental)


⚠️ Data builders are experimental and subject to change. If you have feedback on them, please let us know via

or in the
Kotlin Slack community
.

generates models for your and parsers that create instances of these models from your network responses. Sometimes though, during tests or in other occasions, it is useful to instantiate models manually with known values. Doing so is not as straightforward as it may seem, especially when are used. Creating operationBased models requires instantiating every as well as choosing an appropriate __typename for each composite type. Data builders make this easier by providing builders that match the structure of the Json .

Note: previous versions of Apollo Kotlin used test builders. Data builders are a simpler version of test builders that also plays nicer with custom scalars. If you're looking for the test builders documentation, it's still available

.

Enabling data builders

To enable data, set the generateDataBuilders option to true:

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

This generates a builder for each composite type in your schema as well as a helper Data {} function for each of your .

Example usage

Let's say we're building a test that uses a mocked result of the following :

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

Here's how we can use the corresponding data builder for that mocked result:

@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)
}

In this example, the hero is a Human object with specified values for firstName and age. The values for lastNameand height are automatically populated with mock values. Similarly, values for the ship's speed, the 1st friend's last name and 2nd friend's first name are automatically populated.

You can replace buildHuman above with buildDroid to create a Droid object instead.

Aliases

Because data builders are schema-based and are defined in your queries, there is no way for the codegen to generate builder for them. Instead, you'll need to specify them explicitly.

Given a like this:

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

You can generate a fake data model like so:

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

@skip and @include directives

By default, the data builders match the types in your schema. If a is non-null you will either have to provide a value or let the default provide one. This is an issue for @skip and @include where a might be absent even if it is non-nullable. To account for this case, use the same syntax as for .

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

You can generate a fake data model like so:

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

Configuring default field values

To assign default values to , data builders use an implementation of the FakeResolver interface. By default, they use an instance of DefaultFakeResolver.

The DefaultFakeResolver gives each String the field's name as its default value, and it increments a counter as it assigns default values for Int . It defines similar default behavior for other types.

You can create your own FakeResolver implementation (optionally delegating to DefaultTestResolver for a head start). You then pass this implementation as a parameter to the Data function, like so:

// A TestResolver 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 Ints
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 = myTestResolver) {
hero = buildHuman {
firstName = "John"
}
}
// Unspecified Int field is -1
assertEquals(-1, data.hero.age)
}
Previous
Mocking GraphQL responses
Next
UI Tests
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company