Search code examples
scalacontainsscalatest

FlatSpec displays strange behaviour while using 'should contain only'


I have a simple test where i test if a specific array only contains 2 items.

testArray should contain only (item1, item2)

item1 is a Java object which has a field that can only be set with a setter, not through the constructor.

if i instantiate object 1 like this:

val item1 = new Item1("value1")
item1.setScheme("value2)

The test succeeds saying it does contain the object.

However if i instantiate object1 like this:

val item1 = new Item1("value1") {
   setScheme("value2")
}

the test fails. While the scheme value is still set in the same manner. A println(item1) result in item1(value='value1', scheme='value2') in both cases

Does anyone know why FlatSpec treats these cases differently?

The code for Item1 (slightly Renamed fields for compliancy reasons)

public class Item1 extends LanguageTokenizedString {
  private static final long serialVersionUID = -8903312231226570431L;
  protected String scheme;

  public Item1() {
  }

  public Item1(String value) {
    super(value);
  }

  public Item1(String value, String language) throws InvalidLanguageTokenException {
    super(value, language);
  }

  public Item1(String value, Locale locale) throws InvalidLanguageTokenException {
    super(value, locale);
  }

  public Item1(String value, String language, String scheme) throws InvalidLanguageTokenException {
    super(value, language);
    this.setScheme(scheme);
  }

  public Item1(String value, Locale locale, String scheme) throws InvalidLanguageTokenException {
    super(value, locale);
    this.setScheme(scheme);
  }

  public String getScheme() {
    return this.scheme;
  }

  public final void setScheme(String scheme) {
    this.scheme = scheme;
  }

  public boolean equals(Object obj) {
    boolean equals = false;
    if (obj != null) {
      if (obj == this) {
        equals = true;
      } else if (obj.getClass().equals(this.getClass())) {
        Item1 other = (Item1)obj;
        equals = (new EqualsBuilder()).append(this.value, other.value).append(this.scheme, other.scheme).append(this.schemeId, other.schemeId).append(this.language, other.language).isEquals();
      }
    }

    return equals;
  }

  public boolean shallowEquals(Object obj) {
    boolean shequals = false;
    if (obj != null) {
      if (obj == this) {
        shequals = true;
      } else if (obj.getClass().equals(this.getClass())) {
        Item1 other = (Item1)obj;
        shequals = (new EqualsBuilder()).append(this.value, other.value).append(this.scheme, other.scheme).append(this.schemeId, other.schemeId).isEquals();
      }
    }

    return shequals;
  }

  public int hashCode() {
    return (new HashCodeBuilder(23, 29)).append(this.value).append(this.scheme).append(this.schemeId).append(this.language).toHashCode();
  }

  public boolean isComplete() {
    return true;
  }
}

Solution

  • getClass on anonymous subclass differs from getClass on base class. For example

    val itemA = new Item1("value1")
    itemA.setScheme("value2")
    
    val itemB = new Item1("value1") {
      setScheme("value2")
    }
    
    println(itemA.getClass)
    println(itemB.getClass)
    println(itemA.getClass == itemB.getClass)
    

    should output

    class example.Item1
    class example.HelloSpec$$anon$1
    false
    

    where we see getClass differs for the two. This makes overridden Item1.equals fail on the following check

    if (obj.getClass().equals(this.getClass())) ...
    

    which makes ScalaTests's equality assertions fail.