All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.truth.Subject Maven / Gradle / Ivy

Go to download

Relocates org.truth0:truth to com.google.truth:truth. See http://maven.apache.org/guides/mini/guide-relocation.html

The newest version!
/*
 * Copyright (c) 2011 Google, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.common.truth;

import static com.google.common.truth.StringUtil.format;

import com.google.common.annotations.GwtIncompatible;

import java.lang.reflect.Field;

/**
 * Propositions for arbitrarily typed subjects and for properties
 * of Object
 *
 * @author David Saff
 * @author Christian Gruber ([email protected])
 */
public class Subject,T> {
  protected final FailureStrategy failureStrategy;
  private final T subject;
  private String customName = null;

  public Subject(FailureStrategy failureStrategy, T subject) {
    this.failureStrategy = failureStrategy;
    this.subject = subject;
  }

  protected String internalCustomName() {
    return customName;
  }

  /**
   * Renames the subject so that this name appears in the error messages in place of string
   * representations of the subject.
   */
  @SuppressWarnings("unchecked")
  public S named(String name) {
    if (name == null) {
      // TODO: use check().withFailureMessage... here?
      throw new NullPointerException("Name passed to named() cannot be null.");
    }
    this.customName = name;
    return (S)this;
  }

  /**
   * Soft-deprecated in favor of {@link #named(String)}.
   */
  public S labeled(String label) {
    return named(label);
  }

  public void is(Object other) {
    isEqualTo(other);
  }

  public void isNull() {
    if (getSubject() != null) {
      failWithoutSubject("is a null reference");
    }
  }

  public void isNotNull() {
    if (getSubject() == null) {
      failWithoutSubject("is a non-null reference");
    }
  }

  public void isEqualTo(Object other) {
    if (getSubject() == null) {
      if(other != null) {
        fail("is equal to", other);
      }
    } else {
      if (!getSubject().equals(other)) {
        fail("is equal to", other);
      }
    }
  }

  public void isNotEqualTo(Object other) {
    if (getSubject() == null) {
      if(other == null) {
        fail("is not equal to", (Object)null);
      }
    } else {
      if (getSubject().equals(other)) {
        fail("is not equal to", other);
      }
    }
  }

  public void isA(Class clazz) {
    if (clazz == null) {
      throw new NullPointerException("clazz");
    }
    if (!Platform.isInstanceOfType(getSubject(), clazz)) {
      if (getSubject() != null) {
        failWithBadResults("is an instance of", clazz.getName(),
            "is an instance of", getSubject().getClass().getName());
      } else {
        fail("is an instance of", clazz.getName());
      }
    }
  }

  public void isNotA(Class clazz) {
    if (clazz == null) {
      throw new NullPointerException("clazz");
    }
    if (getSubject() == null) {
      return; // null is not an instance of clazz.
    }
    if (Platform.isInstanceOfType(getSubject(), clazz)) {
      failWithRawMessage("%s expected not to be an instance of %s, but was.",
          getDisplaySubject(), clazz.getName());
    }
  }

  protected T getSubject() {
    return subject;
  }

  protected String getDisplaySubject() {
    return (customName == null)
        ? "<" + getSubject() + ">"
        : "\"" + this.customName + "\"";
  }

  /**
   * A convenience for implementers of {@link Subject} subclasses to use other truth
   * {@code Subject} wrappers within their own propositional logic.
   */
  protected TestVerb check() {
    return new TestVerb(failureStrategy);
  }

  /**
   * Assembles a failure message and passes such to the FailureStrategy
   *
   * @param verb the proposition being asserted
   * @param messageParts the expectations against which the subject is compared
   */
  protected void fail(String verb, Object... messageParts) {
    StringBuilder message = new StringBuilder("Not true that ");
    message.append(getDisplaySubject()).append(" ").append(verb);
    for (Object part : messageParts) {
      message.append(" <").append(part).append(">");
    }
    failureStrategy.fail(message.toString());
  }

  /**
   * Assembles a failure message and passes it to the FailureStrategy
   *
   * @param verb the proposition being asserted
   * @param messageParts the expectations against which the subject is compared
   */
  protected void failWithBadResults(String verb, Object expected, String failVerb, Object actual) {
    String message = format("Not true that %s %s <%s>. It %s <%s>",
            getDisplaySubject(),
            verb,
            expected,
            failVerb,
            ((actual == null) ? "null reference" : actual));
     failureStrategy.fail(message);
  }

  /**
   * Assembles a failure message with an alternative representation of the wrapped subject
   * and passes it to the FailureStrategy
   *
   * @param verb the proposition being asserted
   * @param expected the expected value of the proposition
   * @param actual the custom representation of the subject to be reported in the failure.
   */
  protected void failWithCustomSubject(String verb, Object expected, Object actual) {
    String message = format("Not true that <%s> %s <%s>",
        ((actual == null) ? "null reference" : actual),
        verb,
        expected);
    failureStrategy.fail(message);
  }

  /**
   * Assembles a failure message without a given subject and passes it to the FailureStrategy
   *
   * @param verb the proposition being asserted
   */
  protected void failWithoutSubject(String verb) {
    String subject = this.customName == null ? "the subject" : "\"" + customName + "\"";
    failureStrategy.fail(format("Not true that %s %s", subject, verb));
  }

  /**
   * Passes through a failure message verbatim.  Used for {@link Subject} subclasses which
   * need to provide alternate language for more fit-to-purpose error messages.
   *
   *
   * @param message the message template to be passed to the failure.  Note, this method only
   *     guarantees to process {@code %s} tokens.  It is not guaranteed to be compatible
   *     with {@code String.format()}.  Any other formatting desired (such as floats or
   *     scientific notation) should be performed before the method call and the formatted
   *     value passed in as a string.
   * @param paramters the object parameters which will be applied to the message template.
   */
  protected void failWithRawMessage(String message, Object ... parameters) {
    failureStrategy.fail(format(message, parameters));
  }

  @GwtIncompatible("java.lang.reflect.Field")
  public HasField hasField(final String fieldName) {
    final T subject = getSubject();
    if (subject == null) {
      failureStrategy.fail("Cannot determine a field name from a null object.");
      // Needed for Expect and other non-terminal failure strategies
      return new HasField() {
        @Override public void withValue(Object value) {
          Subject.this.fail("Cannot test the presence of a value in a null object.");
        }
      };
    }
    final Class subjectClass = subject.getClass();
    final Field field;
    try {
      field = ReflectionUtil.getField(subjectClass, fieldName);
      field.setAccessible(true);
    } catch (NoSuchFieldException e) {
      StringBuilder message = new StringBuilder("Not true that ");
      message.append("<").append(subjectClass.getSimpleName()).append(">");
      message.append(" has a field named <").append(fieldName).append(">");
      failureStrategy.fail(message.toString());

      // Needed for Expect and other non-terminal failure strategies
      return new HasField() {
        @Override public void withValue(Object value) {
          Subject.this.fail("Cannot test the presence of a value in a non-present field.");
        }
      };
    }
    return new HasField() {
      @Override public void withValue(Object expected) {
        try {
          Object actual = field.get(subject);
          if (expected == actual || (expected != null && expected.equals(actual))) {
            return;
          } else {
            StringBuilder message = new StringBuilder("Not true that ");
            message.append("<").append(subjectClass.getSimpleName()).append(">'s");
            message.append(" field <").append(fieldName).append(">");
            message.append(" contains expected value <").append(expected).append(">.");
            message.append(" It contains value <").append(actual).append(">");
            failureStrategy.fail(message.toString());
          }
        } catch (IllegalArgumentException e) {
          throw new RuntimeException(
              "Error checking field " + fieldName + " while testing for value " + expected);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(
              "Cannot access field " + fieldName + " to test for value " + expected);
        }
      }
    };
  }

  @GwtIncompatible("java.lang.reflect.Field")
  public static interface HasField {
    /**
     * Supplementary assertion in which a present field can be tested
     * to determine if it contains a given value.
     */
    void withValue(Object value);
  }

  /**
   * @deprecated This method is not a proposition, but the default Object equality method.
   *     Testing code should use "is" or "isEqualTo" propositions for equality tests.
   */
  @Deprecated
  @Override public boolean equals(Object o) {
    isEqualTo(o);
    return false;
  }

  /**
   * @deprecated Equals/Hashcode is not supported on Subjects. Their only use is as a holder of
   *     propositions. Use of equals() is deprecated and forwards to isEqualTo() and
   *     hashCode() is disallowed.
   */
  @Deprecated
  @Override public int hashCode() {
    throw new UnsupportedOperationException(""
        + "Equals/Hashcode is not supported on Subjects. Their only use is as a holder of "
        + "propositions. Use of equals() is deprecated and forwards to isEqualTo() and "
        + "hashCode() is disallowed.");
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy