Подтвердить что ты не робот

Теория Юнита для хэш-кода/равна контракту

Следующий класс служит универсальным тестером для контракта equals/hashCode. Это часть среды, разработанной для взрослых.

  • О чем вы думаете?
  • Как я могу (сильный) протестировать этот класс?
  • Это хорошее использование теорий Junit?

Класс:

@Ignore
@RunWith(Theories.class)
public abstract class ObjectTest {

    // For any non-null reference value x, x.equals(x) should return true
    @Theory
    public void equalsIsReflexive(Object x) {
        assumeThat(x, is(not(equalTo(null))));
        assertThat(x.equals(x), is(true));
    }

    // For any non-null reference values x and y, x.equals(y) 
    // should return true if and only if y.equals(x) returns true.
    @Theory
    public void equalsIsSymmetric(Object x, Object y) {
        assumeThat(x, is(not(equalTo(null))));
        assumeThat(y, is(not(equalTo(null))));
        assumeThat(y.equals(x), is(true));
        assertThat(x.equals(y), is(true));
    }

    // For any non-null reference values x, y, and z, if x.equals(y)
    // returns true and y.equals(z) returns true, then x.equals(z) 
    // should return true.
    @Theory
    public void equalsIsTransitive(Object x, Object y, Object z) {
        assumeThat(x, is(not(equalTo(null))));
        assumeThat(y, is(not(equalTo(null))));
        assumeThat(z, is(not(equalTo(null))));
        assumeThat(x.equals(y) && y.equals(z), is(true));
        assertThat(z.equals(x), is(true));
    }

    // For any non-null reference values x and y, multiple invocations
    // of x.equals(y) consistently return true  or consistently return
    // false, provided no information used in equals comparisons on
    // the objects is modified.
    @Theory
    public void equalsIsConsistent(Object x, Object y) {
        assumeThat(x, is(not(equalTo(null))));
        boolean alwaysTheSame = x.equals(y);

        for (int i = 0; i < 30; i++) {
            assertThat(x.equals(y), is(alwaysTheSame));
        }
    }

    // For any non-null reference value x, x.equals(null) should
    // return false.
    @Theory
    public void equalsReturnFalseOnNull(Object x) {
        assumeThat(x, is(not(equalTo(null))));
        assertThat(x.equals(null), is(false));
    }

    // Whenever it is invoked on the same object more than once 
    // the hashCode() method must consistently return the same 
    // integer.
    @Theory
    public void hashCodeIsSelfConsistent(Object x) {
        assumeThat(x, is(not(equalTo(null))));
        int alwaysTheSame = x.hashCode();

        for (int i = 0; i < 30; i++) {
            assertThat(x.hashCode(), is(alwaysTheSame));
        }
    }

    // If two objects are equal according to the equals(Object) method,
    // then calling the hashCode method on each of the two objects
    // must produce the same integer result.
    @Theory
    public void hashCodeIsConsistentWithEquals(Object x, Object y) {
        assumeThat(x, is(not(equalTo(null))));
        assumeThat(x.equals(y), is(true));
        assertThat(x.hashCode(), is(equalTo(y.hashCode())));
    }

    // Test that x.equals(y) where x and y are the same datapoint 
    // instance works. User must provide datapoints that are not equal.
    @Theory
    public void equalsWorks(Object x, Object y) {
        assumeThat(x, is(not(equalTo(null))));
        assumeThat(x == y, is(true));
        assertThat(x.equals(y), is(true));
    }

    // Test that x.equals(y) where x and y are the same datapoint instance
    // works. User must provide datapoints that are not equal.
    @Theory
    public void notEqualsWorks(Object x, Object y) {
        assumeThat(x, is(not(equalTo(null))));
        assumeThat(x != y, is(true));
        assertThat(x.equals(y), is(false));
    }
}

использование:

import org.junit.experimental.theories.DataPoint;

public class ObjectTestTest extends ObjectTest {

    @DataPoint
    public static String a = "a";
    @DataPoint
    public static String b = "b";
    @DataPoint
    public static String nullString = null;
    @DataPoint
    public static String emptyString = "";
}
4b9b3361

Ответ 1

Одна вещь, которую следует учитывать: тестирование соответствия объекта равному контракту должно включать экземпляры других типов. В частности, проблемы могут появляться с экземплярами подкласса или суперкласса. Джошуа Блох дает отличное объяснение связанных ошибок в Эффективная Java (я повторно использую ссылку duffymo, чтобы он заслужил кредит) см. раздел "Транзитивность" с использованием классов Point и ColorPoint.

Правда, ваша реализация не мешает кому-либо писать тест, который включает экземпляры подкласса, но поскольку ObjectTest - это общий класс, создается впечатление, что все точки данных должны поступать из одного класса (класс, являющийся испытано). Возможно, было бы лучше вообще удалить параметр типа. Просто пища для размышлений.

Ответ 2

Джошуа Блох заключает контракт на хеш-код и равен глава 3 "Эффективная Java" . Похоже, вы много написали. Проверьте документ, чтобы увидеть, пропустил ли я что-нибудь.

Ответ 3

Возможно, я что-то упустил, но критерий equalsIsSymmetric на самом деле проверен только правильно, если у вас есть DataPoints, которые имеют одинаковые значения (например, String a = "a"; String a2 = "a";) В противном случае этот тест выполняется только тогда, когда 2 параметра являются одним экземпляром (т.е. EqualsIsSymmetric (a, a);). На самом деле вы снова проверяете, если они равны требованиям "рефлексивного", а не симметричному требованию.

Ответ 4

Теория notEqualsWorks (Object x, Object y) неверна: два разных экземпляра могут по-прежнему быть логически равными по методу их равных; вы предполагаете, что экземпляры логически различны, если они разные ссылки.

Используя ваш собственный пример выше, два разных datapoints ниже (a!= a2), тем не менее, равны, но не проходят тест notEqualsWorks:

@DataPoint
public static String a = "a";
@DataPoint
public static String a2 = new String("a");

Ответ 5

Метод equalsWorks(Object x, Object y) выполняет тот же тест, что и equalsIsReflexive(Object x). Его следует удалить.

Я также считаю, что notEqualsWorks(Object x, Object y) следует удалить, так как он не позволяет делать другие теории с точками данных, которые равны, даже считали, что все тестирование связано с наличием таких объектов.

Без таких точек данных рефлексивность - это единственное, что проверено.