It’s not a problem to test class for which you can create an instance and execute implemented methods. Little problem starts when you would like to test class without an instance of this class. My way to test abstract classes is to create instance of the class as a mock via Mockito and simulate the behaviour.

In this test I used JUnit 4, Mockito and Hamcrest frameworks. All of them you should add to your Gradle build script:

apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
}

view raw
build.gradle
hosted with ❤ by GitHub

To test abstract class we should create at least one. Here is an example class:

package com.karollotkowski.game;
public abstract class CombatSkills {
public int attack(final AttackType attackType) {
return attackType == AttackType.PHYSICAL ? fight() : spell();
}
protected abstract int fight();
protected abstract int spell();
}

view raw
CombatSkills.java
hosted with ❤ by GitHub

Definition of the enum used in the implementation:

package com.karollotkowski.game;
public enum AttackType {
PHYSICAL, MAGICAL
}

view raw
AttackType.java
hosted with ❤ by GitHub

And simple Warrior implementation of the abstract class:

package com.karollotkowski.game;
import java.util.Random;
public class WarriorSkills extends CombatSkills {
private static final int STRENGTH = 10;
private static final int MANA = 0;
@Override
protected int fight() {
return new Random().ints(0, STRENGTH).findFirst().getAsInt();
}
@Override
protected int spell() {
return MANA;
}
}

view raw
WarriorSkills.java
hosted with ❤ by GitHub

Tests for the WarriorSkills class are obvious, so I will skip to the CombatSkills unit tests.

package com.karollotkowski.game;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class CombatSkillsTest {
@Mock(answer = Answers.CALLS_REAL_METHODS)
private CombatSkills combatSkills;
private final Fixtures fixtures = new Fixtures();
@Test
public void willFightWhenAttackIsPhysical() {
// given
doReturn(fixtures.fightDamage).when(combatSkills).fight();
// when
final int damage = combatSkills.attack(AttackType.PHYSICAL);
// then
assertThat("Fight damage should match", damage, is(fixtures.fightDamage));
verify(combatSkills).fight();
verify(combatSkills, times(0)).spell();
}
@Test
public void willSpellWhenAttackIsMagical() {
// given
doReturn(fixtures.spellDamage).when(combatSkills).spell();
// when
final int damage = combatSkills.attack(AttackType.MAGICAL);
// then
assertThat("Spell damage should match", damage, is(fixtures.spellDamage));
verify(combatSkills, times(0)).fight();
verify(combatSkills).spell();
}
@Test
public void willSpellWhenAttackIsNotDefined() {
// given
doReturn(fixtures.fightDamage).when(combatSkills).fight();
doReturn(fixtures.spellDamage).when(combatSkills).spell();
// when
final int damage = combatSkills.attack(fixtures.attackTypeNotDefined);
// then
assertThat("Spell damage should match", damage, is(fixtures.spellDamage));
verify(combatSkills, times(0)).fight();
verify(combatSkills).spell();
}
private class Fixtures {
private final int spellDamage = 2;
private final int fightDamage = 7;
private final AttackType attackTypeNotDefined = null;
}
}

The most important part is preparation of the mock with answer attribute: CALLS_REAL_METHODS

@Mock(answer = Answers.CALLS_REAL_METHODS)
private CombatSkills combatSkills;

As an alternative you can use Spy feature from Mockito instead of Mock.

@Spy
private CombatSkills combatSkills;

Now you can execute Gradle wrapper test script to check are all tests pass.

./gradlew test

Output:

:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
BUILD SUCCESSFUL
Total time: 6.082 secs

Any insight and other solutions are welcome.