javainterfacejunit5multiple-inheritancegoogle-truth

How should I write Google-Truth Subjects for an object heirarchy that uses interfaces for multiple-inheritance?


The situation

I'm writing tests for a personal project (in Java17 using JUnit5/Jupiter and Google Truth ) where I use the multiple-inheritance of interfaces to define classes. For example:

Example

public interface Taggable {
    public String getTagName();

    public String getSimpleContent();
    
    // ... and more
}


public interface CreatureContainer {
    public Collection<Creature> getCreatures();

    public boolean addCreature(Creature c);

    public void announce(String message);

    // ... and more
}

public class Area implements Taggable, CreatureContainer { // possibly more
   private String name;
   private Set<Creature> creatures;

   // Constructor etc

   @Override
   public String getTagName() {
        return this.getClass().getName();
   }

   @Override
   public String getSimpleContent() {
        return this.name;
   }

   @Override
   public Collection<Creature> getCreatures() {
      return creatures;
   }

   // ...etc

}

Options

As the Google Truth Subject class is a CLASS and there's no interfaces to use to mirror my structure, I cannot write something like:

public class TaggableSubject extends Subject {};
public class CreatureContainerSubject extends Subject {};
public class AreaSubject extends TaggableSubject, CreatureContainerSubject {}; // ERROR!!

I can see a few options:

Option 1

I write one Subject for each of Taggable, CreatureContainer, and Area. In the AreaSubject I include methods such as

public TaggableSubject asTaggable() {
    return check("this").that(actual); // just give it a raw actual?
}


public CreatureContainerSubject asCreatureContainer() {
   return check("this").that(actual);
}

Option 2

I only write the AreaSubject, and include a method for each thing in from the inherited interfaces.

Such as:


public StringSubject tagName() {
   return check("getTagName()").that(actual.getTagName());
}

// or similarly
public void hasTagName(String tagName) {
    this.tagName().isEqualTo(tagName);
}

public void hasCreature(Creature c) {
    check("getCreatures()").that(actual.getCreatures()).contains(c);
}

The Question

To keep moving forward, I'm going to go with the first option, but I'd still like to know:

Which of the options is more true to the Google Truth paradigm? Or is there a better option?

Thank you.


Solution

  • I don't remember any specific precedent for this, but I would say:

    If you do go for the ideal user experience, then you can optionally avoid duplicating the implementations of the assertion methods into AreaSubject by delegating to your other Subject implementations:

    public class AreaSubject extends Subject {
      public void hasCreature(Creature c) {
        check("this").about(creatureContainers()).that(actual).hasCreature(c);
      }
      ...
    }
    

    That doesn't actually simplify the code if the implementations are already simple. And it actually makes the failure message slightly worse because it makes the message include "this." (We've considered providing a way to avoid that, but we haven't done so. For now, we employ hacks where we want it.) But if you have complex implementations that you want to delegate to, then it can be handy.


    If you're curious: I'd mentioned precedent:

    (Sorry for letting this question sit an entire month.)