Try out using Truth library
diff --git a/build.gradle b/build.gradle
index 8b5728f..69fea88 100644
--- a/build.gradle
+++ b/build.gradle
@@ -143,7 +143,8 @@
 
                 // Test dependencies.
                 junit: 'junit:junit:4.11',
-                mockito: 'org.mockito:mockito-core:1.9.5'
+                mockito: 'org.mockito:mockito-core:1.9.5',
+                truth: 'com.google.truth:truth:0.28'
         ]
 
         // Determine the correct version of Jetty ALPN boot to use based
diff --git a/core/src/main/java/io/grpc/Deadline.java b/core/src/main/java/io/grpc/Deadline.java
index 31d03ab..8ef421f 100644
--- a/core/src/main/java/io/grpc/Deadline.java
+++ b/core/src/main/java/io/grpc/Deadline.java
@@ -41,7 +41,7 @@
  * An absolute deadline in system time.
  */
 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/262")
-public class Deadline {
+public final class Deadline implements Comparable<Deadline> {
 
   /**
    * Create a deadline that will expire at the specified offset from the current system clock.
@@ -108,7 +108,7 @@
     if (offset == 0) {
       return this;
     }
-    return new Deadline(deadlineNanos,  units.toNanos(offset), isExpired());
+    return new Deadline(deadlineNanos, units.toNanos(offset), isExpired());
   }
 
   /**
@@ -141,4 +141,15 @@
   public String toString() {
     return timeRemaining(TimeUnit.NANOSECONDS) + " ns from now";
   }
+
+  @Override
+  public int compareTo(Deadline that) {
+    long diff = this.deadlineNanos - that.deadlineNanos;
+    if (diff < 0) {
+      return -1;
+    } else if (diff > 0) {
+      return 1;
+    }
+    return 0;
+  }
 }
diff --git a/core/src/test/java/io/grpc/CallOptionsTest.java b/core/src/test/java/io/grpc/CallOptionsTest.java
index 2be1057..b3eec9c 100644
--- a/core/src/test/java/io/grpc/CallOptionsTest.java
+++ b/core/src/test/java/io/grpc/CallOptionsTest.java
@@ -31,20 +31,17 @@
 
 package io.grpc;
 
-import static io.grpc.DeadlineTest.assertDeadlineEquals;
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 import static io.grpc.DeadlineTest.extractRemainingTime;
+import static io.grpc.testing.DeadlineSubject.deadline;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.MINUTES;
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Objects;
-import com.google.common.util.concurrent.MoreExecutors;
 
 import io.grpc.Attributes.Key;
 import io.grpc.internal.SerializingExecutor;
@@ -54,7 +51,6 @@
 import org.junit.runners.JUnit4;
 
 import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
 
 /** Unit tests for {@link CallOptions}. */
 @RunWith(JUnit4.class)
@@ -70,86 +66,103 @@
 
   @Test
   public void defaultsAreAllNull() {
-    assertNull(CallOptions.DEFAULT.getDeadline());
-    assertNull(CallOptions.DEFAULT.getAuthority());
-    assertEquals(Attributes.EMPTY, CallOptions.DEFAULT.getAffinity());
-    assertNull(CallOptions.DEFAULT.getExecutor());
+    assertThat(CallOptions.DEFAULT.getDeadline()).isNull();
+    assertThat(CallOptions.DEFAULT.getAuthority()).isNull();
+    assertThat(CallOptions.DEFAULT.getAffinity()).isEqualTo(Attributes.EMPTY);
+    assertThat(CallOptions.DEFAULT.getExecutor()).isNull();
   }
 
   @Test
   public void allWiths() {
-    assertSame(sampleAuthority, allSet.getAuthority());
-    assertSame(sampleDeadline, allSet.getDeadline());
-    assertSame(sampleAffinity, allSet.getAffinity());
+    assertThat(allSet.getAuthority()).isSameAs(sampleAuthority);
+    assertThat(allSet.getDeadline()).isSameAs(sampleDeadline);
+    assertThat(allSet.getAffinity()).isSameAs(sampleAffinity);
   }
 
   @Test
   public void noStrayModifications() {
-    assertTrue(equal(allSet,
-        allSet.withAuthority("blah").withAuthority(sampleAuthority)));
-    assertTrue(equal(allSet,
-        allSet.withDeadline(Deadline.after(314, NANOSECONDS))
-            .withDeadline(sampleDeadline)));
-    assertTrue(equal(allSet,
-          allSet.withAffinity(Attributes.EMPTY).withAffinity(sampleAffinity)));
+    assertThat(equal(allSet, allSet.withAuthority("blah").withAuthority(sampleAuthority)))
+        .isTrue();
+    assertThat(
+        equal(allSet,
+            allSet.withDeadline(Deadline.after(314, NANOSECONDS)).withDeadline(sampleDeadline)))
+        .isTrue();
+    assertThat(
+        equal(allSet,
+            allSet.withAffinity(Attributes.EMPTY).withAffinity(sampleAffinity)))
+        .isTrue();
   }
 
   @Test
   public void mutation() {
     Deadline deadline = Deadline.after(10, SECONDS);
     CallOptions options1 = CallOptions.DEFAULT.withDeadline(deadline);
-    assertNull(CallOptions.DEFAULT.getDeadline());
-    assertEquals(deadline, options1.getDeadline());
+    assertThat(CallOptions.DEFAULT.getDeadline()).isNull();
+    assertThat(deadline).isSameAs(options1.getDeadline());
+
     CallOptions options2 = options1.withDeadline(null);
-    assertEquals(deadline, options1.getDeadline());
-    assertNull(options2.getDeadline());
+    assertThat(deadline).isSameAs(options1.getDeadline());
+    assertThat(options2.getDeadline()).isNull();
   }
 
   @Test
   public void mutateExecutor() {
-    Executor executor = MoreExecutors.directExecutor();
+    Executor executor = directExecutor();
     CallOptions options1 = CallOptions.DEFAULT.withExecutor(executor);
-    assertNull(CallOptions.DEFAULT.getExecutor());
-    assertSame(executor, options1.getExecutor());
+    assertThat(CallOptions.DEFAULT.getExecutor()).isNull();
+    assertThat(executor).isSameAs(options1.getExecutor());
+
     CallOptions options2 = options1.withExecutor(null);
-    assertSame(executor, options1.getExecutor());
-    assertNull(options2.getExecutor());
+    assertThat(executor).isSameAs(options1.getExecutor());
+    assertThat(options2.getExecutor()).isNull();
   }
 
   @Test
   public void withDeadlineAfter() {
-    Deadline deadline = CallOptions.DEFAULT.withDeadlineAfter(1, MINUTES).getDeadline();
+    Deadline actual = CallOptions.DEFAULT.withDeadlineAfter(1, MINUTES).getDeadline();
     Deadline expected = Deadline.after(1, MINUTES);
-    assertDeadlineEquals(deadline, expected, 10, MILLISECONDS);
+
+    assertAbout(deadline()).that(actual).isWithin(10, MILLISECONDS).of(expected);
   }
 
   @Test
-  public void toStringMatches() {
-    assertEquals("CallOptions{deadline=null, authority=null, "
-        + "affinity={}, executor=null, compressorName=null}", CallOptions.DEFAULT.toString());
+  public void toStringMatches_noDeadline_default() {
+    String expected = "CallOptions{deadline=null, authority=authority, affinity={sample=blah}, "
+        + "executor=class io.grpc.internal.SerializingExecutor, compressorName=null}";
+    String actual = allSet
+        .withDeadline(null)
+        .withExecutor(new SerializingExecutor(directExecutor()))
+        .toString();
 
-    // Deadline makes it hard to check string for equality.
-    assertEquals("CallOptions{deadline=null, authority=authority, "
-        + "affinity={sample=blah}, executor=class io.grpc.internal.SerializingExecutor, "
-        + "compressorName=null}",
-        allSet.withDeadline(null)
-            .withExecutor(new SerializingExecutor(MoreExecutors.directExecutor())).toString());
+    assertThat(actual).isEqualTo(expected);
+  }
 
-    assertDeadlineEquals(
-        allSet.getDeadline(), extractRemainingTime(allSet.toString()), 20, MILLISECONDS);
+  @Test
+  public void toStringMatches_noDeadline() {
+    assertThat("CallOptions{deadline=null, authority=null, "
+        + "affinity={}, executor=null, compressorName=null}")
+            .isEqualTo(CallOptions.DEFAULT.toString());
+  }
+
+  @Test
+  public void toStringMatches_withDeadline() {
+    assertAbout(deadline()).that(extractRemainingTime(allSet.toString()))
+        .isWithin(20, MILLISECONDS).of(allSet.getDeadline());
   }
 
   @Test
   @SuppressWarnings("deprecation")
   public void withDeadlineNanoTime() {
     CallOptions opts = CallOptions.DEFAULT.withDeadlineNanoTime(System.nanoTime());
-    assertNotNull(opts.getDeadlineNanoTime());
-    assertTrue(opts.getDeadlineNanoTime() <= System.nanoTime());
-    assertTrue(opts.getDeadline().isExpired());
+    assertThat(opts.getDeadlineNanoTime()).isNotNull();
+    assertThat(opts.getDeadlineNanoTime()).isAtMost(System.nanoTime());
+    assertThat(opts.getDeadline().isExpired()).isTrue();
 
-    assertDeadlineEquals(opts.getDeadline(), Deadline.after(0, SECONDS), 20, TimeUnit.MILLISECONDS);
+    assertAbout(deadline()).that(opts.getDeadline())
+        .isWithin(20, MILLISECONDS).of(Deadline.after(0, SECONDS));
   }
 
+  // TODO(carl-mastrangelo): consider making a CallOptionsSubject for Truth.
   private static boolean equal(CallOptions o1, CallOptions o2) {
     return Objects.equal(o1.getDeadline(), o2.getDeadline())
         && Objects.equal(o1.getAuthority(), o2.getAuthority())
diff --git a/core/src/test/java/io/grpc/DeadlineTest.java b/core/src/test/java/io/grpc/DeadlineTest.java
index fbc3169..5581c63 100644
--- a/core/src/test/java/io/grpc/DeadlineTest.java
+++ b/core/src/test/java/io/grpc/DeadlineTest.java
@@ -31,6 +31,8 @@
 
 package io.grpc;
 
+import static com.google.common.truth.Truth.assertAbout;
+import static io.grpc.testing.DeadlineSubject.deadline;
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.junit.Assert.assertEquals;
@@ -39,11 +41,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.google.common.truth.Truth;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.math.BigInteger;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -67,14 +70,14 @@
 
   @Test
   public void shortDeadlineEventuallyExpires() throws Exception {
-    Deadline deadline = Deadline.after(100, TimeUnit.MILLISECONDS);
-    assertTrue(deadline.timeRemaining(TimeUnit.NANOSECONDS) > 0);
-    assertFalse(deadline.isExpired());
+    Deadline d = Deadline.after(100, TimeUnit.MILLISECONDS);
+    assertTrue(d.timeRemaining(TimeUnit.NANOSECONDS) > 0);
+    assertFalse(d.isExpired());
     Thread.sleep(101);
 
-    assertTrue(deadline.isExpired());
-    assertFalse(deadline.timeRemaining(TimeUnit.NANOSECONDS) > 0);
-    assertDeadlineEquals(deadline, Deadline.after(0, SECONDS), maxDelta, NANOSECONDS);
+    assertTrue(d.isExpired());
+    assertFalse(d.timeRemaining(TimeUnit.NANOSECONDS) > 0);
+    assertAbout(deadline()).that(d).isWithin(maxDelta, NANOSECONDS).of(Deadline.after(0, SECONDS));
   }
 
   @Test
@@ -86,10 +89,10 @@
 
   @Test
   public void pastDeadlineIsExpired() {
-    Deadline deadline = Deadline.after(-1, TimeUnit.SECONDS);
-    assertTrue(deadline.isExpired());
+    Deadline d = Deadline.after(-1, TimeUnit.SECONDS);
+    assertTrue(d.isExpired());
 
-    assertDeadlineEquals(deadline, Deadline.after(-1, SECONDS), maxDelta, NANOSECONDS);
+    assertAbout(deadline()).that(d).isWithin(maxDelta, NANOSECONDS).of(Deadline.after(-1, SECONDS));
   }
 
   @Test
@@ -159,35 +162,47 @@
   public void toString_exact() {
     Deadline d = Deadline.after(0, TimeUnit.MILLISECONDS);
 
-    assertDeadlineEquals(d, extractRemainingTime(d.toString()), maxDelta, NANOSECONDS);
+    assertAbout(deadline()).that(extractRemainingTime(d.toString()))
+        .isWithin(maxDelta, NANOSECONDS).of(d);
   }
 
   @Test
   public void toString_after() {
     Deadline d = Deadline.after(-1, TimeUnit.HOURS);
 
-    assertDeadlineEquals(d, extractRemainingTime(d.toString()), maxDelta, NANOSECONDS);
+    assertAbout(deadline()).that(extractRemainingTime(d.toString()))
+        .isWithin(maxDelta, NANOSECONDS).of(d);
+  }
+
+  @Test
+  public void compareTo_greater() {
+    Deadline d1 = Deadline.after(10, TimeUnit.SECONDS);
+    Deadline d2 = Deadline.after(10, TimeUnit.SECONDS);
+    // Assume that two calls take more than 1 ns.
+    Truth.assertThat(d2).isGreaterThan(d1);
+  }
+
+  @Test
+  public void compareTo_less() {
+    Deadline d1 = Deadline.after(10, TimeUnit.SECONDS);
+    Deadline d2 = Deadline.after(10, TimeUnit.SECONDS);
+    // Assume that two calls take more than 1 ns.
+    Truth.assertThat(d1).isLessThan(d2);
+  }
+
+  @Test
+  public void compareTo_same() {
+    Deadline d1 = Deadline.after(10, TimeUnit.SECONDS);
+    Deadline d2 = d1.offset(0, TimeUnit.SECONDS);
+    Truth.assertThat(d1).isEquivalentAccordingToCompareTo(d2);
   }
 
   @Test
   public void toString_before() {
     Deadline d = Deadline.after(10, TimeUnit.SECONDS);
 
-    assertDeadlineEquals(d, extractRemainingTime(d.toString()), maxDelta, NANOSECONDS);
-  }
-
-  /**
-   * Asserts two deadlines are roughly equal.
-   */
-  public static void assertDeadlineEquals(
-      Deadline expected, Deadline actual, long delta, TimeUnit timeUnit) {
-    // This is probably overkill, but easier than thinking about overflow.
-    BigInteger actualTimeRemaining = BigInteger.valueOf(actual.timeRemaining(NANOSECONDS));
-    BigInteger expectedTimeRemaining = BigInteger.valueOf(expected.timeRemaining(NANOSECONDS));
-    BigInteger deltaNanos = BigInteger.valueOf(timeUnit.toNanos(delta));
-    if (actualTimeRemaining.subtract(expectedTimeRemaining).abs().compareTo(deltaNanos) > 0) {
-      throw new AssertionError(String.format("%s != %s", expected, actual));
-    }
+    assertAbout(deadline()).that(extractRemainingTime(d.toString()))
+        .isWithin(maxDelta, NANOSECONDS).of(d);
   }
 
   static Deadline extractRemainingTime(String deadlineStr) {
diff --git a/testing/build.gradle b/testing/build.gradle
index 4e9ca73..8070789 100644
--- a/testing/build.gradle
+++ b/testing/build.gradle
@@ -3,5 +3,6 @@
     compile project(':grpc-core'),
             project(':grpc-stub'),
             libraries.junit,
-            libraries.mockito
+            libraries.mockito,
+            libraries.truth
 }
diff --git a/testing/src/main/java/io/grpc/testing/DeadlineSubject.java b/testing/src/main/java/io/grpc/testing/DeadlineSubject.java
new file mode 100644
index 0000000..ba436eb
--- /dev/null
+++ b/testing/src/main/java/io/grpc/testing/DeadlineSubject.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.grpc.testing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.common.truth.ComparableSubject;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
+
+import io.grpc.Deadline;
+
+import java.math.BigInteger;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nullable;
+
+/**
+ * Propositions for {@link Deadline} subjects.
+ */
+public final class DeadlineSubject extends ComparableSubject<DeadlineSubject, Deadline> {
+  private static final SubjectFactory<DeadlineSubject, Deadline> deadlineFactory =
+      new DeadlineSubjectFactory();
+
+  public static SubjectFactory<DeadlineSubject, Deadline> deadline() {
+    return deadlineFactory;
+  }
+
+  private DeadlineSubject(FailureStrategy failureStrategy, Deadline subject) {
+    super(failureStrategy, subject);
+  }
+
+  /**
+   * Prepares for a check that the subject is deadline within the given tolerance of an
+   * expected value that will be provided in the next call in the fluent chain.
+   */
+  @CheckReturnValue
+  public TolerantDeadlineComparison isWithin(final long delta, final TimeUnit timeUnit) {
+    return new TolerantDeadlineComparison() {
+      @Override
+      public void of(Deadline expected) {
+        Deadline actual = getSubject();
+        checkNotNull(actual, "actual value cannot be null. expected=%s", expected);
+
+        // This is probably overkill, but easier than thinking about overflow.
+        BigInteger actualTimeRemaining = BigInteger.valueOf(actual.timeRemaining(NANOSECONDS));
+        BigInteger expectedTimeRemaining = BigInteger.valueOf(expected.timeRemaining(NANOSECONDS));
+        BigInteger deltaNanos = BigInteger.valueOf(timeUnit.toNanos(delta));
+        if (actualTimeRemaining.subtract(expectedTimeRemaining).abs().compareTo(deltaNanos) > 0) {
+          failWithRawMessage(
+              "%s and <%s> should have been within <%sns> of each other",
+              getDisplaySubject(),
+              expected,
+              deltaNanos);
+        }
+      }
+    };
+  }
+
+  // TODO(carl-mastrangelo):  Add a isNotWithin method once there is need for one.  Currently there
+  // is no such method since there is no code that uses it, and would lower our coverage numbers.
+
+  /**
+   * A partially specified proposition about an approximate relationship to a {@code deadline}
+   * subject using a tolerance.
+   */
+  public abstract class TolerantDeadlineComparison {
+
+    private TolerantDeadlineComparison() {}
+
+    /**
+     * Fails if the subject was expected to be within the tolerance of the given value but was not
+     * <i>or</i> if it was expected <i>not</i> to be within the tolerance but was. The expectation,
+     * subject, and tolerance are all specified earlier in the fluent call chain.
+     */
+    public abstract void of(Deadline expectedDeadline);
+
+    /**
+     * Do not call this method.
+     *
+     * @throws UnsupportedOperationException always
+     * @deprecated {@link Object#equals(Object)} is not supported on TolerantDeadlineComparison
+     *     If you meant to compare deadlines, use {@link #of(Deadline)} instead.
+     */
+    @Deprecated
+    @Override
+    public boolean equals(@Nullable Object o) {
+      throw new UnsupportedOperationException(
+          "If you meant to compare deadlines, use .of(Deadline) instead.");
+    }
+
+    /**
+     * Do not call this method.
+     *
+     * @throws UnsupportedOperationException always
+     * @deprecated {@link Object#hashCode()} is not supported on TolerantDeadlineComparison
+     */
+    @Deprecated
+    @Override
+    public int hashCode() {
+      throw new UnsupportedOperationException("Subject.hashCode() is not supported.");
+    }
+  }
+
+  private static final class DeadlineSubjectFactory
+      extends SubjectFactory<DeadlineSubject, Deadline>  {
+    @Override
+    public DeadlineSubject getSubject(FailureStrategy fs, Deadline that) {
+      return new DeadlineSubject(fs, that);
+    }
+  }
+}