Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New #simple() method still requires #usingGetClass() when superclass has fields #316

Closed
eggilbert opened this issue Jun 15, 2020 · 4 comments
Labels

Comments

@eggilbert
Copy link
Contributor

What steps will reproduce the problem?

Apologies if I'm misunderstanding the new capabilities, but it seems like whenever a class extends another one that has its own #equals() and #hashCode(), the #usingGetClass() method is still needed in conjunction with #simple() which suppresses the strict inheritance check.

What is the code that triggers this problem?

package org.mypackage;

import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;

import java.util.Objects;

class MyTest {
    @Test
    void concreteFromAbstractBase() {
        EqualsVerifier.simple()
            .forClass(ConcreteFromAbstractBase.class)
            .verify();
    }

    @Test
    void concreteFromConcreteBase() {
        // This works
        EqualsVerifier.simple()
            .forClass(ConcreteFromConcreteBase.class)
            .usingGetClass()
            .verify();

        // This dies with #simple() but without #usingGetClass()
        EqualsVerifier.simple()
            .forClass(ConcreteFromConcreteBase.class)
            .verify();
    }

    @Test
    void concreteFromIntermediateNoFinal() {
        // This works
        EqualsVerifier.simple()
            .forClass(ConcreteFromIntermediateNoFinal.class)
            .usingGetClass()
            .verify();

        // This dies with #simple() but without #usingGetClass()
        EqualsVerifier.simple()
            .forClass(ConcreteFromIntermediateNoFinal.class)
            .verify();
    }

    @Test
    void concreteFromIntermediateWithFinal() {
        // This works
        EqualsVerifier.simple()
            .forClass(ConcreteFromIntermediateWithFinal.class)
            .usingGetClass()
            .verify();

        // This dies with #simple() but without #usingGetClass()
        EqualsVerifier.simple()
            .forClass(ConcreteFromIntermediateWithFinal.class)
            .verify();
    }

    public abstract static class AbstractBase {
        protected void helper() {
            // No fields here
        }
    }

    public abstract static class AbstractIntermediateWithFinal extends AbstractBase {
        private final String finalBaseField;

        public AbstractIntermediateWithFinal(String finalBaseField) {
            this.finalBaseField = finalBaseField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AbstractIntermediateWithFinal that = (AbstractIntermediateWithFinal) o;
            return Objects.equals(finalBaseField, that.finalBaseField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(finalBaseField);
        }
    }

    public abstract static class AbstractIntermediateNoFinal extends AbstractBase {
        private String intermediateFieldOne;
        private String intermediateFieldTwo;

        public String getIntermediateFieldOne() {
            return intermediateFieldOne;
        }

        public void setIntermediateFieldOne(String intermediateFieldOne) {
            this.intermediateFieldOne = intermediateFieldOne;
        }

        public String getIntermediateFieldTwo() {
            return intermediateFieldTwo;
        }

        public void setIntermediateFieldTwo(String intermediateFieldTwo) {
            this.intermediateFieldTwo = intermediateFieldTwo;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AbstractIntermediateNoFinal that = (AbstractIntermediateNoFinal) o;
            return Objects.equals(intermediateFieldOne, that.intermediateFieldOne) &&
                Objects.equals(intermediateFieldTwo, that.intermediateFieldTwo);
        }

        @Override
        public int hashCode() {
            return Objects.hash(intermediateFieldOne, intermediateFieldTwo);
        }
    }

    public static class ConcreteBase {
        private String concreteBaseField;

        public String getConcreteBaseField() {
            return concreteBaseField;
        }

        public void setConcreteBaseField(String concreteBaseField) {
            this.concreteBaseField = concreteBaseField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ConcreteBase that = (ConcreteBase) o;
            return Objects.equals(concreteBaseField, that.concreteBaseField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(concreteBaseField);
        }
    }

    public static class ConcreteFromConcreteBase extends ConcreteBase {
        private String concreteField;

        public String getConcreteField() {
            return concreteField;
        }

        public void setConcreteField(String concreteField) {
            this.concreteField = concreteField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;
            ConcreteFromConcreteBase that = (ConcreteFromConcreteBase) o;
            return Objects.equals(concreteField, that.concreteField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), concreteField);
        }
    }

    public static class ConcreteFromAbstractBase extends AbstractBase {
        private String concreteField;

        public String getConcreteField() {
            return concreteField;
        }

        public void setConcreteField(String concreteField) {
            this.concreteField = concreteField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ConcreteFromAbstractBase that = (ConcreteFromAbstractBase) o;
            return Objects.equals(concreteField, that.concreteField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(concreteField);
        }
    }

    public static class ConcreteFromIntermediateWithFinal extends AbstractIntermediateWithFinal {
        private String concreteField;

        public ConcreteFromIntermediateWithFinal(String finalBaseField) {
            super(finalBaseField);
        }

        public String getConcreteField() {
            return concreteField;
        }

        public void setConcreteField(String concreteField) {
            this.concreteField = concreteField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;
            ConcreteFromIntermediateWithFinal that = (ConcreteFromIntermediateWithFinal) o;
            return Objects.equals(concreteField, that.concreteField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), concreteField);
        }
    }

    public static class ConcreteFromIntermediateNoFinal extends AbstractIntermediateNoFinal {
        private String concreteField;

        public String getConcreteField() {
            return concreteField;
        }

        public void setConcreteField(String concreteField) {
            this.concreteField = concreteField;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;
            ConcreteFromIntermediateNoFinal that = (ConcreteFromIntermediateNoFinal) o;
            return Objects.equals(concreteField, that.concreteField);
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), concreteField);
        }
    }
}

What error message or stack trace does EqualsVerifier give?

java.lang.AssertionError: EqualsVerifier found a problem in class org.mypackage.MyTest$ConcreteFromConcreteBase.
-> Symmetry:
  org.mypackage.MyTest$ConcreteFromConcreteBase@35d442
does not equal superclass instance
  org.mypackage.MyTest$ConcreteBase@1ae85

What did you expect?

Per Changelog 3.4, I expected to be able to just call EqualsVerifier.simple().forClass(klazz.class).verify() for objects with an IDE-generated #equals() and #hashCode() that don't need other options (like prefab values).

Warning.STRICT_INHERITANCE is noted as being suppressed automatically by #simple(). And Warning.STRICT_INHERITANCE is also noted as removing the requirement for #usingGetClass().

Previously I had been using the following:

EqualsVerifier.forClass(klazz.class)
    .usingGetClass()
    .suppress(Warning.NONFINAL_FIELDS)
    .verify();

Which version of EqualsVerifier are you using?

3.4

Please provide any additional information below.

@jqno
Copy link
Owner

jqno commented Jun 15, 2020

Thanks for reporting this. I agree, this scenario should have been covered with .simple(). I'll fix this!

@jqno
Copy link
Owner

jqno commented Jun 15, 2020

Version 3.4.1 is released which should fix this :)

@jqno jqno closed this as completed Jun 15, 2020
@eggilbert
Copy link
Contributor Author

Thanks, 3.4.1 is working as expected. 👍

@jqno
Copy link
Owner

jqno commented Jun 15, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants