Comparator Verifier #

Comparison method violates its general contract! #

Ако сте написали компаратор, който не спазва договора, описан в Javadoc, ето как обикновено разбирате за това:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
    at java.util.Arrays.sort(Arrays.java:472)
    at java.util.Collections.sort(Collections.java:155)
    ...

Нека го оправим.

Първи стъпки #

Comparator Verifier е интуитивно API за тестване дали дадена имплементация на Comparator спазва изисквания договор, чрез написване на обикновен юнит тест.

Добавяне на Зависимост #

Добавете comparatorverifier към вашия файл pom.xml.

<dependency>
    <groupId>io.github.tomregan</groupId>
    <artifactId>comparatorverifier</artifactId>
    <version>1.0.0</version>
    <scope>test</scope>
</dependency>

Добавете comparatorverifier към вашия файл build.gradle.

dependencies {
    testImplementation 'io.github.tomregan:comparatorverifier:1.0.0'
}

Добавете comparatorverifier към вашия файл build.gradle.kts.

dependencies {
    testImplementation("io.github.tomregan:comparatorverifier:1.0.0")
}

Напишете Тест #

Напишете юнит тест за вашия компаратор.

@Test
void comparatorContract() {
    ComparatorVerifier.forComparator(FooComparator.class).verify();
}

Допълнително използване #

Примерно използване с JUnit #

Comparator Verifier позволява конфигурируема верификация, включваща както строги, така и по-свободни проверки.

По-долу са показани примерни JUnit тестове с различни конфигурации на Comparator Verifier.

По-свободна верификация (Permissive) #

import co.mp.ComparatorVerifier;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
final class FooComparatorTest {

    @Test
    void comparator_contract_is_met() {
        ComparatorVerifier.forComparator(FooComparator.class)
            .permissive()
            .withExamples(new Foo("x"), new Foo("y"))
            .verify();
    }
}

Строга верификация (Strict) #

import co.mp.ComparatorVerifier;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
final class FooComparatorTest {

    @Test
    void comparator_can_be_serialized_in_a_tree_set() {
        ComparatorVerifier.forComparator(FooComparator.class)
            .strict()
            .withExamples(new Foo("a"), new Foo("b"))
            .verify();
    }
}

Верификация с потиснати предупреждения #

Компаратор, който не отговаря на договора за Comparator, не е безопасен за използване с Java колекции.

Обмислете използване на permissive() или strict() за промяна на поведението на верификацията.

import co.mp.ComparatorVerifier;
import co.mp.Warning;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
final class FooComparatorTest {

    @Test
    void comparator_contract_is_met() {
        ComparatorVerifier.forComparator(FooComparator.class)
            .withExamples(new Foo("1"), new Foo("2"), new Foo("3"))
            .suppress(Warning.TRANSITIVITY)
            .verify();
    }
}