AssertJ: NoSuchMethodException в softAssert

Я экспериментировал с assertJ в качестве альтернативы текущей библиотеке утверждений hamcrest, которая используется в моем командном проекте. Пока у меня есть следующий класс:

Animal.java

package com.assertions.demo.assertj;

public class Animal {

private Species species;

//no arg constructor
public Animal(){}

public Animal(Species species) {
    this.species = species;
}

public Species getSpecies() {
    return this.species;
}

public enum Species{ 
    DOG(4, true, Environment.LAND), 
    PIGEON(2, false, Environment.AIR), 
    OCTOPUS(8, false, Environment.SEA), 
    ALLIGATOR(4, true, Environment.AMPHIBIOUS);

    private int numLegs;
    private boolean hasTail;
    private Environment environment;

    private Species(int numLegs, boolean hasTail, Environment environment) {
        this.numLegs = numLegs;
        this.hasTail = hasTail;
        this.environment = environment;
    }

    public int getNumLegs(){
        return this.numLegs;
    }

    public boolean hasTail() {
        return this.hasTail;
    }
    public Environment getEnvironment() {
        return this.environment;
    }
}

public enum Environment {
    LAND, SEA, AIR, AMPHIBIOUS
}

}

И я создал для него следующие пользовательские утверждения:

AnimalAssert.java

package com.assertions.demo.assertj;

import org.assertj.core.api.AbstractAssert;
import com.assertions.demo.assertj.Animal.Environment;

public class AnimalAssert extends AbstractAssert<AnimalAssert, Animal> {

public AnimalAssert(Animal actual, Class<AnimalAssert> selfType) {
    super(actual, selfType);
}

public static AnimalAssert assertThat(Animal actual) {
    return new AnimalAssert(actual, AnimalAssert.class);
}

public AnimalAssert hasTail(boolean hasTail) {
    isNotNull();

    if(actual.getSpecies().hasTail() != hasTail) {
        String expectedHasTail = hasTail ? "have a tail" : "not have a tail";
        String actualHasTail = actual.getSpecies().hasTail() ? "does" : "does not";
        failWithMessage("Expected animal to " + expectedHasTail + ", but animal " + actualHasTail + " have a tail");
    }
    return this;
}

public AnimalAssert livesInEnvironment(Environment env) {
    isNotNull();

    if(actual.getSpecies().getEnvironment() != env) {
        failWithMessage("Expected animal environment to be <%s>, but was <%s>", env, actual.getSpecies().getEnvironment());
    }
    return this;
}

public AnimalAssert hasNumberOfLegs(int numLegs) {
    isNotNull();

    if(actual.getSpecies().getNumLegs() != numLegs) {
        failWithMessage("Expected animal to have <%s> legs, but animal has <%s> legs", numLegs, actual.getSpecies().getNumLegs());
    }

    return this;
}
}

В соответствии с функциями AssertJ я предоставил точку входа для вышеупомянутого и пытаюсь добавить функциональность мягкого утверждения:

CustomSoftAssertions.java

package com.demo.assertions.assertj;

import org.assertj.core.api.SoftAssertions;

public class CustomSoftAssertions extends SoftAssertions {

public AnimalAssert assertThat(Animal actual) {
    return proxy(AnimalAssert.class, Animal.class, actual);
}
}

Когда я запускаю следующий тест:

AssertJDemo.java

package com.assertions.demo.assertj;

import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import com.assertions.demo.assertj.Animal.Environment;
import com.assertions.demo.assertj.Animal.Species;

public class AssertJDemo {
    Animal[] testAnimalArray = new Animal[] {
        new Animal(Species.ALLIGATOR),
        new Animal(Species.DOG),
        new Animal(Species.OCTOPUS),
        new Animal(Species.PIGEON)
    };

    @Test
    public void softAssertWithCustomAssertionsTest() {
        Animal dog = testAnimalArray[1];
        CustomSoftAssertions softAssert = new CustomSoftAssertions();

        softAssert.assertThat(dog).hasTail(false);
        softAssert.assertThat(dog).livesInEnvironment(Environment.AIR);
        softAssert.assertThat(dog).hasNumberOfLegs(8);
    }
}

Я получаю следующую трассировку стека:

org.assertj.core.internal.cglib.core.CodeGenerationException: java.lang.NoSuchMethodException-->com.assertions.demo.assertj.AnimalAssert$$EnhancerByCGLIB$$b5c6715f.<init>(com.assertions.demo.assertj.Animal)
at org.assertj.core.internal.cglib.core.ReflectUtils.getConstructor(ReflectUtils.java:313)
at org.assertj.core.internal.cglib.proxy.Enhancer$EnhancerFactoryData.<init>(Enhancer.java:420)
at org.assertj.core.internal.cglib.proxy.Enhancer.wrapCachedClass(Enhancer.java:709)
at org.assertj.core.internal.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
at org.assertj.core.internal.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91)
at org.assertj.core.internal.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.assertj.core.internal.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at org.assertj.core.internal.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at org.assertj.core.internal.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)
at org.assertj.core.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at org.assertj.core.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at org.assertj.core.internal.cglib.proxy.Enhancer.create(Enhancer.java:324)
at org.assertj.core.api.SoftProxies.create(SoftProxies.java:42)
at org.assertj.core.api.AbstractSoftAssertions.proxy(AbstractSoftAssertions.java:31)
at com.assertions.demo.assertj.CustomSoftAssertions.assertThat(CustomSoftAssertions.java:18)
at com.assertions.demo.assertj.AssertJDemo.softAssertWithCustomAssertionsTest(AssertJDemo.java:202)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.NoSuchMethodException: com.assertions.demo.assertj.AnimalAssert$$EnhancerByCGLIB$$b5c6715f.<init>(com.assertions.demo.assertj.Animal)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.assertj.core.internal.cglib.core.ReflectUtils.getConstructor(ReflectUtils.java:309)
... 39 more

Я что-то делаю не так с библиотекой assertJ, или, может быть, я нарушаю что-то специфическое для java, чего я не заметил? Я использую Java 8 с assertJ 3.8.0

РЕДАКТИРОВАТЬ: я обновил класс Animal без конструктора arg на основе первого ответа (cglib требует его), но возникает то же исключение


person jbailie1991    schedule 22.06.2017    source источник
comment
Следуя EDIT, это потому, что исключением является запрос конструктора, который принимает Animal в качестве аргумента .. @davidxxx Вы знаете, что можете редактировать свои ответы, поэтому нет необходимости добавлять несколько ответов.   -  person    schedule 22.06.2017
comment
@RC, значит, для утверждения животного нужен конструктор с Animal в качестве аргумента? Новичок в AssertJ, так что все еще ищу свой способ обойти это   -  person jbailie1991    schedule 22.06.2017
comment
Собственно, из того, что я прочитал, я забыл вызвать softAssert.assertAll (). Я также настроил код, чтобы более точно следовать примеру (удалил параметр selfType и жестко запрограммированный AnimalAssert.class в вызове конструктора). когда я добавил эту строку и изменил класс assert, тест работает как надо   -  person jbailie1991    schedule 22.06.2017
comment
@RC вы правы. Я буду осторожнее с этим.   -  person davidxxx    schedule 22.06.2017
comment
@ jbailie1991 На самом деле я не могу понять, почему у вас эта проблема. В моем последнем ответе я предложил обход, который не требуется, если вы хотите расширить SoftAssertions.   -  person davidxxx    schedule 22.06.2017


Ответы (1)


Вы видели assertj-examples, есть рабочий пример настраиваемых мягких утверждений:

https://github.com/joel-costigliola/assertj-examples/blob/master/assertions-examples/src/test/generated-assertions/org/assertj/SoftAssertions.java

https://github.com/joel-costigliola/assertj-examples/blob/master/assertions-examples/src/test/java/org/assertj/examples/GeneratedSoftAssertionsExamples.java

person Joel Costigliola    schedule 23.06.2017