ProtoTruth is an extension of the Truth library which lets you make assertions about Protobufs, a rich data interchange format. To get started, follow the steps below, or for Lite proto follow our Lite Proto instructions:
-
Add the appropriate dependency to your build:
Maven:
<dependency> <groupId>com.google.truth.extensions</groupId> <artifactId>truth-proto-extension</artifactId> <version>1.4.4</version> </dependency>
Gradle:
repositories { mavenCentral() } dependencies { testImplementation "com.google.truth.extensions:truth-proto-extension:1.4.4" }
Use
truth-liteproto-extension
if you are using the lite version of Protobufs. Note that this extension lacks many of the features in the full extension due to the lack of runtime descriptors. You’ll also need to import LiteProtoTruth instead of ProtoTruth. -
Add a static import:
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
NOTE:
ProtoTruth.assertThat
andTruth.assertThat
can both be statically imported in the same file. Java handles overloaded static imports much like overloads defined in the same class. -
Assert away!
MyProto expected = MyProto.newBuilder().addBar(3).addBar(5).setFoo("qux").build(); MyProto actual = MyProto.newBuilder().addBar(5).addBar(3).setFoo("quizzux").build(); assertThat(actual).ignoringRepeatedFieldOrder().isEqualTo(expected);
This assertion yields the following failure message:
Not true that messages compare equal. Differences were found: modified: foo: "qux" -> "quizzux"
Supported Use Cases
Support exists for:
- Assertions on singular, generic Proto 2 messages.
- Assertions on singular, generic Proto 3 messages.
- Assertions on Iterables, Maps, and Multimaps of such messages.
- Tailored assertions for the
Any
type, for Proto 3. - Lite protos, though support is nominal
Support does not currently exist (yet) for:
- Tailored assertions for extension fields for Proto 2.
- Tailored assertions for the
UnknownFieldSet
. - Tailored assertions for utility protos, including
FieldMasks
.
Support will not exist for:
- gRPC, for which Truth is not an appropriate home. Mocking libraries are better suited to testing RPC interactions.
Strict default behavior
By default, the extension is fairly strict and requires you to explicitly state your test assumptions. It will:
- not ignore field absence; to change this behavior, use:
ignoringFieldAbsence()
- not ignore repeated field order; to change this behavior, use:
ignoringRepeatedFieldOrder()
- not report mismatches only; to change this behavior, use:
reportingMismatchesOnly()
- use exact equality for floating-point fields; to change this behavior,
use:
usingDoubleTolerance
and/orusingFloatTolerance
- check all fields; to change this behavior, use a combination of:
ignoringFields(int...)
ignoringFieldDescriptors(FieldDescriptor...)
withPartialScope(FieldScope)
comparingExpectedFieldsOnly()
ignoringFieldScope(FieldScope)
In summary, the default isEqualTo()
behavior of ProtoSubject
is
behaviorally identical to Subject
behavior. However, you will get much
better error messaging in the event of a failure.
Getting better failure messages by enabling pairing of Iterable
elements
When an assertion involving an Iterable
of protos fails, the failure messages
can often be hard to understand. If you know of some key function which uniquely
indexes the expected protos, you can use the displayingDiffsPairedBy
method to
tell ProtoTruth about it. For example, if you have a proto called MyRecord
,
and you’re making an assertion about an Iterable<MyRecord>
, and the expected
records have unique values of an id
field, then you could write this:
assertThat(actualRecordProtos)
.displayingDiffsPairedBy(MyRecord::getId)
.containsExactlyElementsIn(expectedRecords);
If this assertion fails, the failure message will pair up any missing and
unexpected elements by their IDs. For example, it might tell you that the actual
Iterable
was missing an element with ID 2, that it had an unexpected element
with ID 3, or that the element with ID 4 wasn’t equivalent to the one it
expected… and in this last case, it will also show a field-by-field diff
between the proto it got and the one it expected.
(If an assertion about a Map
fails, the failure message will automatically
match up any missing and unexpected entries using their keys. You can think of
the displayingDiffsPairedBy
method as providing an equivalent for an assertion
about an Iterable
. Note that this won’t affect whether the test passes or
fails.)
Lite Proto and Android Support
Nominal support exists for Lite protos, commonly used in Android code due to the size of the full-proto runtime. However, most features available for full protos are not available for lite protos because runtime descriptors are essential for doing dynamic comparisons. To use lite protos with proto truth:
-
Add a dependency:
Maven:
<dependency> <groupId>com.google.truth.extensions</groupId> <artifactId>truth-liteproto-extension</artifactId> <version>1.4.4</version> </dependency>
Gradle:
repositories { mavenCentral() } dependencies { testImplementation "com.google.truth.extensions:truth-liteproto-extension:1.4.4" }
-
Add a static import:
import static com.google.common.truth.extensions.proto.LiteProtoTruth.assertThat;
-
And assert away!