0

I have an abstract class which contains logic in concrete methods:

public abstract class AbstractEventHandler implements EventHandler {

  private final Dependency dependency;

  public AbstractEventHandler(Dependency dependency) {
    this.dependency = dependency;
  }

  @Override
  void handleEvent(Event event) {
    dependency.doSomeWork();
    [...]
    doHandleEvent(event);
    [...]
  }

  @Override
  void handleOtherEvent(OtherEvent otherEvent) {
    dependency.doOtherWork();
    [...]
    doHandleOtherEvent(event);
    [...]
  }

  protected abstract doHandleEvent(event);
  protected abstract doHandleOtherEvent(event);
}

Explored solutions to test my abstract class:

  • create a dummy implementation of the abstract class (good point for constructors Mocking an abstract class and injecting classes with Mockito annotations?)
  • test the handleEvent(event) logic in concrete classes but I would have to duplicate the test in every concrete classes (or once, but in which class?)
  • use PowerMock...
  • use Mockito to instantiate an implementation of the abstract class and call real methods to test logic in concrete methods

I chose the Mockito solution since it's quick and short (especially if the abstract class contains a lot of abstract methods).

@ExtendWith(MockitoExtension.class)
class AbstractEventHandlerTests {

  @Mock
  private Dependency dependency;

  @InjectMocks
  @Mock(answer = Answers.CALLS_REAL_METHODS)
  private AbstractEventHandler abstractEventHandler;

Since @InjectMocks is not permitted on a field already annotated with @Mock, how can I inject mocked dependencies in my abstract class?

Florian Lopes
  • 1,093
  • 1
  • 13
  • 20
  • 1
    What do you want to achieve by adding `@Mock` on your class-under-test `abstractEventHandler`? Just omit this annotation and test your methods. What am I missing? – mle Apr 18 '19 at 11:03
  • I use `@Mock` on my class-under-test to quickly instantiate an implementation of my `AbstractEventHandler` abstract class, without having to define every abstract methods in an anonymous inner class. – Florian Lopes Apr 18 '19 at 11:42
  • But your `AbstractEventHandler` is not labeled `abstract` though it has `abstract` methods. Your code shown will not compile. Please adapt it by either making the `AbstractEventHandler` `abstract` or by removing the `abstract` method stubs. – mle Apr 18 '19 at 15:03
  • Fixed typo; however, it's not essential for the question here. – Florian Lopes Apr 19 '19 at 08:24
  • And what about your `protected abstract doHandleOtherEvent();` without parameter? To which method belongs the call some lines above: `doHandleOtherEvent(event);`? – mle Apr 19 '19 at 16:48
  • 1
    Well to keep it short, IMHO it does not really make sense to test a class that could never be used on its own and which contains calls to not yet populated hence abstract methods. I would test such abstract classes always through and with their implementations. Otherwise you end up with a green test of your abstract class, well, cool but stop! What is the real meaning of it? None. – mle Apr 19 '19 at 16:50

1 Answers1

0

To reply to comments, I initially wanted to test the behavior of the concrete method in the abstract class because this method has to do some work (using the provided dependency), before calling implementations. To test this behavior, I either had to write tests in each implementation (can have a lot of implementations) or to test it once via the abstract class. I ended up using the first solution: create an anonymous inner class as an implementation of the abstract class. Thanks.

Florian Lopes
  • 1,093
  • 1
  • 13
  • 20