0

I have recently encountered multiple articles with title Everytime a mock returns a mock a fairy dies And I ran into exact same situation while using factory class in my code. I am writing a sample Java code here to explain in detail what I mean.

@RequiredArgsConstructor
class Battle{
  private final GunFactory;

  public Damage attack(Bullet availableBullet, Enemy enemy){
    Gun gun = gunFactory.create(availableBullet);
    return gun.shoot(enemy);
  }
}

class BattleTest{
  private GunFactory gunFactory = mock(GunFactory.class);
  private Gun gun = mock(Gun.class);
  private Battle battle = new Battle(gunFactory);

  @Test
  public void attack(){
    Enemy anyEnemy = new Enemy(strength=10);
    Bullet anyBullet = new Bullet("Machine gun bullet");
    Damage anyDamage = new Damage(100);

    when(gunFactory.create(anyAvailableBullet)).thenReturn(gun);
    when(gun.shoot(anyEnemy)).thenReturn(anyDamage);

    assertThat(battle.attack(anyBullte, anyEnemy), is(anyDamage));
  }
}

As you can see in the sample (ugly) code my factory is a mock and is returning another mock. So it got me wondering if there is anyway to avoid this. I tried moving one class higher, so that the GunFactory sends Gun object as a parameter to attack function, but I would still run into same issue when I test the other class.

Is there a way to avoid this. Or with factory pattern is this inevitable?

4
  • In a typical Battle the number of guns and stocks of ammo are fixed. Why not pass in the actual information to the Battle class rather than a factory? It seems really odd that you would create a gun for the provided bullet every time. Commented Mar 5, 2020 at 15:03
  • The actual code is rather different, but for obvious reasons I could not share it here. So I made up code to give a basic picture. Imagine that I can only figure out which gun I need to use depending on the Enemy (not bullet). How would you approach the problem?
    – bharathp
    Commented Mar 5, 2020 at 15:14
  • Write code that doesn't require mocks to test. Commented Mar 5, 2020 at 15:22
  • This test is effectively testing the inner implementation of Battle.attack, when it should really relate the expected Damage result from the Bullet and Enemy arguments.
    – Lee
    Commented Mar 5, 2020 at 15:25

1 Answer 1

7

I can think of a number of ways:

  1. You can make your factory use a dictionary of injected types. Then you can use the real factory and inject a mock
  2. You can flatten your OOP code so that you don't have any child objects.
  3. You can stop mocking the child objects and concentrate on mocking only things that persist state

I think 3 is your best option here. You could argue that a test of battle which uses a real GunFactory and Gun is not a unit test, but all you really need to mock for a good test is the data.

So for example say gun factory reads the gun data from a database, that's the only thing you need to mock to have repeatable fast tests. You can leave the discussion about whether its a 'real unit test' to the philosophers

3
  • Your answer is a good reminder that unit tests need not to be "pure" to be useful!
    – T. Sar
    Commented Mar 5, 2020 at 18:07
  • 1
    But the more complex they get, you might have to rethink portions of your design. A unit test exposes everything a person needs to keep straight in their brain when they read your code. Commented Mar 6, 2020 at 14:15
  • @BerinLoritsch yes! I’d also say that unit tests help make your code readable. Commented Mar 7, 2020 at 4:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.