0

Alpha - parent with beta as child

public class Alpha {
  Beta beta;

  public Alpha(int argument) {}

  void start() {
    beta = createBeta();
  }

  Beta createBeta() {
    return new Beta(this);
  }
}

Beta - child of alpha, has charlie

public class Beta {
  Alpha alpha;
  Charlie charlie;

  public Beta(Alpha alpha) {
    this.alpha = alpha;
    this.charlie = createCharlie();
  }

  Charlie createCharlie() {
    return new Charlie().shuffle();
  }
}

Charlie - has a list, which is usually shuffled in production

public class Charlie {
  List<Integer> list = new ArrayList<Integer>();

  public Charlie() {
    for (int i = 0; i < 6; i++) {
      list.add(i);
    }
  }

  public Charlie shuffle() {
    Collections.shuffle(list);
    return this;
  }

  @Override
  public String toString() {
    return list.toString();
  }
}

AlphaTest [need help with this test] - want to try different variations of shuffling to see how alpha/beta would react.

public class AlphaTest {

  Charlie special = new Charlie();

  @Test
  public void testSpecialCharlie() {
    Alpha alpha = Mockito.spy(new Alpha(0));
    Beta beta = Mockito.spy(alpha.createBeta());

    Mockito.when(alpha.createBeta()).thenReturn(beta);
    Mockito.when(beta.createCharlie()).thenReturn(special);

    alpha.start();

    // FAILURE: expected:<[0, 1, 2, 3, 4, 5]> but was:<[0, 4, 1, 5, 3, 2]>
    assertEquals(special.list, alpha.beta.charlie.list);
  }
}

Goal is to test Alpha/Beta with different combinations of Charlie. Not sure what's the best way to do that? This is exact code copied. Open to changing the setup also to facilitate testing. Tried different variations, nothing really working. Would really appreciate help with this.

I am not sure, I tried a bunch of ways (mocking out createCharlie() function, mocking out Charlie class, mocking out shuffle(), moving createCharlie() to parent Alpha class, nothing really works properly or maybe I was missing something. Any help would be really appreciated. Thanks!

Wonder why I can't just do this:

Charlie charlie = Mockito.mock(Charlie.class);
Mockito.when(charlie.shuffle()).thenReturn(special);
  • What do you mean with "nothing really works properly"? – Dario Apr 14 '16 at 05:52
  • Sorry, I meant, I couldn't get it to work, I am really new to testing, so I probably wasn't doing right. I am okay restructuring the setup a little bit also. Please let me know how you would do this? Thanks! – Sam Walker Apr 14 '16 at 06:02
  • Unit testing code like this is quite easy with JMockit (you just declare a `@Mocked Beta` or `@Mocked Charlie` field/parameter and then record/verify whichever invocations, regardless of how instances get created in the CUT)... However, I would ask *why mock* anything here in the first place? Keep in mind, mocking is not to be abused. People like Kent Beck (creator of TDD), Martin Fowler, and other "gurus" don't favor mocking (for good reasons). – Rogério Apr 14 '16 at 15:12
  • I actually have no idea what's the best way to go here. I don't have JMockit set up. Thought this should be straightforward, have limited experience with testing, but have already wasted a few days on it and still can't figure it out :( – Sam Walker Apr 15 '16 at 03:31

2 Answers2

0

I will leave my initial answer below if someone find this usefull.

First, put into Beta in last line of constructor this:

System.out.println("Charlie created: " + this.charlie.hashCode());

After running test you will see that charlie is created multiple times.

First when this is called in test:

Beta beta = Mockito.spy(alpha.createBeta());

and later when this is called:

Mockito.when(alpha.createBeta()).thenReturn(beta);

So Beta keeps reference to real Charlie and because you mocked createCharlie(), when you call your code:

Assert.assertEquals(special.list, alpha.beta.charlie.list);

Real Charlie is called, not mocked one. The way how you implemented your classes is not really great, but if I just answer what you should change in test - you should call this instead:

Assert.assertEquals(special.list, alpha.beta.createCharlie().list);

In that case mocked Charlie will be called and the test will pass.


Previous answer:

Usually when you unit test something, your focus is on a single testing class. You could use @Spy/@InjectMocks. When you will test Beta, you should mock Charlie, when you will test Alpha, you will mock Beta...

I don't know the purpose but strange in your code is that createCharlie() and randomize() returns instance of Charlie. Anyway, you could lazily create Charlie via getter, something like this:

public class Beta {
    private Charlie charlie;

    public Beta() {
    }

    public Charlie getCharlie() {
        if (charlie == null) {
            charlie = new Charlie();
        }
        return charlie;
    }

    public void doSomethingWithCharlie() {
        getCharlie().randomize();
    }
}

You could inject charlie in your test like this:

public class BetaTest{
    @Mock(name="charlie") // variable name in Beta
    private Charlie charlieMock;

    @InjectMocks
    private Beta beta;

    @BeforeMethod
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    public void test1(){
        beta.doSomethingWithCharlie();
    }
}

See also @InjectMocks documentation.

Optionally you could add package protected/public method to Alpha:

Beta createBeta() {
    return new Beta(); 
}

or

Beta getBeta() {
    if(beta==null){
        beta = new Beta();
    }

    return beta;
}

Then you could inject into Alpha real Beta which returns mocked Charlie:

public class AlphaTest { @Mock Charlie charlieMock;

@Spy
Beta beta;

@Spy
Alpha alpha;

public void beforeTest() {
    when(alpha.getBeta()).thenReturn(beta);
    when(beta.getCharlie()).thenReturn(charlie);
}

}

See also this.

Community
  • 1
  • 1
Dario
  • 2,053
  • 21
  • 31
  • Thanks Dario, perhaps I wasn't clear, I added more info to my question. I agree with testing individual classes but in this case unfortunately, Beta is dependent on Alpha and I can't decouple them. Goal is to verify Alpha/Beta interaction with different Charlies, thats why I want to just mock out the creation of charlie so I can write tests against different instances of Charlie. Hope that clarifies a bit? – Sam Walker Apr 14 '16 at 09:35
  • I also appended additional informations to my reply. – Dario Apr 14 '16 at 10:54
  • I tried the second approach, didn't work :( - I am not sure what I am missing, this is what I tried before too. – Sam Walker Apr 15 '16 at 03:40
  • Use debug and verify that mocks are injected – Dario Apr 15 '16 at 04:52
  • Hi Dario, please take another look at the question. That is exact code copied, so you know exactly what I am trying to do. Thank you SO much for taking the time out for this. Really looking forward to your next reply. – Sam Walker Apr 15 '16 at 04:57
  • Check edited answer. There is solution for your trouble. You should add more traces, go with debug over and try to understand what was the problem. – Dario Apr 15 '16 at 05:48
  • Thank you, that helps definitely. Tests pass and I understand much better how this works now. But now my concern is that I don't want to create charlie when I do `Mockito.when(alpha.createBeta()).thenReturn(beta)`, I want to somehow set it up so that `special` is used when I do `alpha.start()` – Sam Walker Apr 15 '16 at 06:12
  • Please see the answer I posted. That solves my problem. Let me know if you have a better way than that. Thanks! – Sam Walker Apr 15 '16 at 06:31
0

Thanks to Dario's answer, I changed Beta class to

public class Beta {
  Alpha alpha;
  Charlie _charlie;

  public Beta(Alpha alpha) {
    this.alpha = alpha;
    this._charlie = getCharlie();
    // PROBLEM: If I use this._charlie here, that will use the wrong charlie!
  }

  Charlie getCharlie() {
    if (_charlie == null) {
      _charlie = new Charlie().shuffle();
    }
    return _charlie;
  }
}

Then, I never access charlie with alpha.beta._charlie and always with getCharlie(), that should solve my problem. Thank you Dario, much appreciated!

  • Added a comment about problem with this approach. What I really need is to setup my code so that the first getCharlie() call returns the `special` instance. – Sam Walker Apr 15 '16 at 07:23