Skip to content

Commit 4df2dc8

Browse files
cpovirkronshapiro
authored andcommitted
Make factValue() reject facts that have keys but no value.
It feels icky to me that anyone would pass null as a magic value, and there are already ways to check that a given key is present. Technically there's no longer a way to test that a given key has no associated value, but it is hard to imagine that people would get this wrong, write tests for it, and fix the bug as a result. (It hasn't come up in Truth itself, at least.) And we can add support later if needed -- always easier than removing support after the fact. Also, add tests for facts that have keys without values. RELNOTES=n/a ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190790320
1 parent 343e7a1 commit 4df2dc8

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

core/src/main/java/com/google/common/truth/TruthFailureSubject.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
* caller tries to access the facts of an error that wasn't produced by Truth.
4141
*/
4242
final class TruthFailureSubject extends ThrowableSubject {
43+
static final Fact HOW_TO_TEST_KEYS_WITHOUT_VALUES =
44+
factWithoutValue(
45+
"To test that a key is present without a value, "
46+
+ "use factKeys().contains(...) or a similar method.");
47+
4348
/*
4449
* TODO(cpovirk): Expose this publicly once it can have the right type. That can't happen until we
4550
* add type parameters to ThrowableSubject, make TruthFailureSubject not extend ThrowableSubject,
@@ -89,12 +94,19 @@ private static ImmutableList<String> getFactKeys(ErrorWithFacts error) {
8994
/**
9095
* Returns a subject for the value with the given name.
9196
*
92-
* <p>The value, if present, is always a string, the {@code String.valueOf} representation of the
93-
* value passed to {@link Fact#fact}.
97+
* <p>The value is always a string, the {@code String.valueOf} representation of the value passed
98+
* to {@link Fact#fact}.
99+
*
100+
* <p>The value is never null:
94101
*
95-
* <p>The value is null in the case of {@linkplain Fact#factWithoutValue facts that have no
96-
* value}. By contrast, facts that have a value that is rendered as "null" (such as those created
97-
* with {@code fact("key", null)}) are considered to have a value, the string "null."
102+
* <ul>
103+
* <li>In the case of {@linkplain Fact#factWithoutValue facts that have no value}, {@code
104+
* factValue} throws an exception. To test for such facts, use {@link #factKeys()}{@code
105+
* .contains(...)} or a similar method.
106+
* <li>In the case of facts that have a value that is rendered as "null" (such as those created
107+
* with {@code fact("key", null)}), {@code factValue} considers them have a string value,
108+
* the string "null."
109+
* </ul>
98110
*
99111
* <p>If the failure under test contains more than one fact with the given key, this method will
100112
* fail the test. To assert about such a failure, use {@linkplain #factValue(String, int) the
@@ -145,9 +157,27 @@ private StringSubject doFactValue(String key, @Nullable Integer index) {
145157
fact("fact count was", factsWithName.size()));
146158
return ignoreCheck().that("");
147159
}
160+
String value = factsWithName.get(firstNonNull(index, 0)).value;
161+
if (value == null) {
162+
if (index == null) {
163+
failWithoutActual(
164+
factWithoutValue("expected to have a value"),
165+
fact("for key", key),
166+
factWithoutValue("but the key was present with no value"),
167+
HOW_TO_TEST_KEYS_WITHOUT_VALUES);
168+
} else {
169+
failWithoutActual(
170+
factWithoutValue("expected to have a value"),
171+
fact("for key", key),
172+
fact("and index", index),
173+
factWithoutValue("but the key was present with no value"),
174+
HOW_TO_TEST_KEYS_WITHOUT_VALUES);
175+
}
176+
return ignoreCheck().that("");
177+
}
148178
StandardSubjectBuilder check =
149179
index == null ? check("factValue(%s)", key) : check("factValue(%s, %s)", key, index);
150-
return check.that(factsWithName.get(firstNonNull(index, 0)).value);
180+
return check.that(value);
151181
}
152182

153183
private static ImmutableList<Fact> factsWithName(ErrorWithFacts error, String key) {

core/src/test/java/com/google/common/truth/TruthFailureSubjectTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.google.common.truth;
1818

1919
import static com.google.common.truth.Fact.fact;
20+
import static com.google.common.truth.Fact.factWithoutValue;
21+
import static com.google.common.truth.TruthFailureSubject.HOW_TO_TEST_KEYS_WITHOUT_VALUES;
2022
import static com.google.common.truth.TruthFailureSubject.truthFailures;
2123
import static org.junit.Assert.fail;
2224

@@ -35,6 +37,11 @@ public void factKeys() {
3537
assertThat(fact("foo", "the foo")).factKeys().containsExactly("foo");
3638
}
3739

40+
@Test
41+
public void factKeysNoValue() {
42+
assertThat(factWithoutValue("foo")).factKeys().containsExactly("foo");
43+
}
44+
3845
@Test
3946
public void factKeysFail() {
4047
expectFailureWhenTestingThat(fact("foo", "the foo")).factKeys().containsExactly("bar");
@@ -78,6 +85,17 @@ public void factValueFailMultipleKeys() {
7885
assertFailureValue("but contained multiple", "[foo: the foo, foo: the other foo]");
7986
}
8087

88+
@Test
89+
public void factValueFailNoValue() {
90+
Object unused = expectFailureWhenTestingThat(factWithoutValue("foo")).factValue("foo");
91+
assertFailureKeys(
92+
"expected to have a value",
93+
"for key",
94+
"but the key was present with no value",
95+
HOW_TO_TEST_KEYS_WITHOUT_VALUES.key);
96+
assertFailureValue("for key", "foo");
97+
}
98+
8199
// factValue(String, int)
82100

83101
@Test
@@ -127,6 +145,19 @@ public void factValueIntFailNotEnoughWithKey() {
127145
assertFailureValue("fact count was", "1");
128146
}
129147

148+
@Test
149+
public void factValueIntFailNoValue() {
150+
Object unused = expectFailureWhenTestingThat(factWithoutValue("foo")).factValue("foo", 0);
151+
assertFailureKeys(
152+
"expected to have a value",
153+
"for key",
154+
"and index",
155+
"but the key was present with no value",
156+
HOW_TO_TEST_KEYS_WITHOUT_VALUES.key);
157+
assertFailureValue("for key", "foo");
158+
assertFailureValue("and index", "0");
159+
}
160+
130161
// other tests
131162

132163
@Test

0 commit comments

Comments
 (0)