Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test overridden methods which call super?

http://stackoverflow.com/questions/6645263/unit-testing-overridden-methods-which-call-super (This question has similar wording but is not the same)

I have something along the lines of:

public class SuperClass {
   private int superClassInteger = 2;

   public void superClassMethod() {
      superClassInteger = 5;
   }

   public int getSuperClassInteger() {
      return superClassInteger();
   }
}

Then in my test I have:

public class SuperClassTest {
   public void testSuperClassMethod() {
       SuperClass superClass = new SuperClass();
       superClass.superClassMethod();
       assertEquals(5, super.getSuperClassInteger())
   }
}

I then have a subclass:

public class SubClass {
   private int subClassInteger = 2;

   public void subClassMethod() {
      super.superClassMethod();
      subClassInteger = 32;
   }

   public int getSuperClassInteger() {
      subClassInteger;
   }
}

Then my test for the subclass:

public class SubClassTest {
   public void testSubClassMethod() {
       SubClass subClass = new SubClass();
       subClass.subClassMethod();
       assertEquals(5, subclass.getSuperClassInteger());
       assertEquals(32, subclass.getSubClassInteger())
   }
}

My problem is that to test the behavior of the subclass, I am repeating the test code I have for the super class. I could take out: assertEquals(5, subclass.getSuperClassInteger()); as I just want to test the business logic of the subclass. However, the problem with this is that if somebody accidentally removes the call to superClassMethod the test will still pass. So I need verify that a call to super is made. What is a common way of testing this use case?

Note: I know composition / strategy pattern vs inheritance helps solve this problem but for that to be the solution you are basically saying that I should NEVER override a method and invoke super in my code (which I find hard to believe there will never be a use for)

like image 695
mogronalol Avatar asked Dec 07 '12 11:12

mogronalol


2 Answers

Use mockito spies. You can check for nearly any method call using that testing Framework.

https://github.com/mockito/mockito

like image 61
sorencito Avatar answered Oct 16 '22 05:10

sorencito


This is probably not the answer you want (i.e not a workaround), but the answer you deserve but you should never override a method and invoke super.

I'm working on a legacy project that does that a lot, it's a nightmare to maintain, to debug and to test.

Instead, it's better to use an abstract class with a template method pattern:

Before

public class SuperClass<T> {
  public void include(T t) {
    validate(T);
    dao.save(T);
  }

  // I've found code like that.
  public void validate(T t) {
  }
}

public class SubClass1 extends SuperClass<MyClass> {
  @Override
  public void include(MyClass mc) {
    doStuff(mc);
    doMoreStuff(mc);
    super.include(mc);
  }

  @Override
  public void validate(MyClass mc) {
    doValidationStuff();
  }
}

public class SubClass2 extends SuperClass<AnotherClass> {
  @Override
  public void include(AnotherClass ac) {
    doDifferentStuff();
    super.include(mc);
  }
}

After

public abstract class AbstractClass<T> {
  abstract void validate(T t);

  // this method can now be tested
  public void include(T t) {
    validate(T);
    dao.save(T);
  }
}

public class Class1 extends AbstractClass<MyClass> {
  public void validate(MyClass mc) {
    doValidationStuff();
    doStuff(mc);
    doMoreStuff(mc);
  }
}

public class Class2 extends AbstractClass<AnotherClass> {
  public void validate(AnotherClass ac) {
    doDifferentStuff();
  }
}

As you can see, this refactoring simplifies maintenance and testing, you don't have to worry about having to mock the superclass method call in the Class1 and Class2 tests, and the code is closed to modification and open to extension, i.e, more maintainable.

like image 37
Tarek Avatar answered Oct 16 '22 06:10

Tarek