0

TDD and Mockito testing newb here... I have recently discovered about injection of mocks of private fields and am trying to get my ideas straight.

It may well be that my thinking on this particular matter is all wrong, but it's just that I often find a situation where I want to test that calling a particular method has resulted in a private field being assigned: before the call it was null, and after the call it is set to an instance of whatever class.

A mocked private field is a subclass of the class in question by the time you start to run your test method, so obviously testing for null is meaningless/doesn't work. Equally, if your method results in an assignment of this field this means that the mock variable has been replaced by a genuine instance. Thus, even if methods are called on it it won't lead to any interaction with the mock variable. In any event, if the test is just to check on the creation and assignment there is no reason you would want to have any interaction with it in the test.

Obviously one answer is to create a public get() method or a public create() method in the app class. But this gives me a bad feeling as it is clearly just being done for testing purposes.

Incidentally there will be those, many no doubt much more experienced than me at TDD, who will say that I'm getting too fine-grained and shouldn't be testing something as specific as assignment of a field. But my understanding of the most extreme approach to TDD is "don't write a line of code without a test". I really am trying to stick with this at this stage as far as possible. With experience maybe I will have greater understanding of how and when to depart from this principle.

It's just that assignment of a field seems to me to be a "class event" (of the host class) as worthy as any other type of event, such as a call of a method on the field once an instance has been assigned to the field. Shouldn't we be able to test for it in some way...? What do the Mockito pros who accept that this may be a legitimate thing to test for do in these circumstances?

later...

The comment from Fabio highlights the tension which, as a newb, I find puzzling, between "never write a line without a test" and "test only public methods": the effect of this is inevitably to expose much more stuff than I want, purely for the sake of testing.

And what he says about "changing tests all the time" I also find problematic: if we inject mocked private fields, and use them, then we are going to have to change the tests if we change the "implementation details". Nor does this idea fill me with dread: a need for change is perceived, you look at the very explanatory names of your existing test methods and decide which ones no longer apply, and of course you create new ones... the testing code, like the app code, will have to change all the time, right?

Isn't the testing code like some sort of die or mould, which guides and constrains the app code but at the same time can be detached from it? By Mockito allowing us to mock private fields a privileged form of access is already granted... who's to say how far this privileged access should rightly extend?

even later...

Jeff Bowman suggests this is a dup with this. I'm not entirely convinced, since I am asking a very specific thing for which it's just possibly there may be a specific answer (some Mockito means of detecting assignment). But his answer there seems very comprehensive and I'm trying to fully understand it all... including the whole dependency injection thing... and the compromises you seem have to make in "distorting" your public interface to make an app class as testable as you want to make it.

Community
  • 1
  • 1
mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • 3
    private fields are implementation details, which can be refactored/deleted. And you don't want change tests every time you refactoring your code. You need to test only public methods/fields of your unit. If you have private field it mean some public method will gonna use it. So when you start write tests for this method you will write your assignment in production code, because if not you will get NullReferenceException. In TDD you write only code that makes your tests green, then after you refactoring it. – Fabio Nov 09 '16 at 21:24
  • thanks... please see my added thoughts... – mike rodent Nov 09 '16 at 21:49
  • Another approach which I found out useful was - in situation where you cannot decide how to proceed - just peek one(in your case mock private field up and test that it assigned properly). Later if you noticed that after some refactoring(not behavior changes) you need to change tests too - you can decide by yourself which decision suit your requirements better – Fabio Nov 09 '16 at 22:03
  • 1
    I believe this is a duplicate of [this question and answer](http://stackoverflow.com/q/27552499/1426891), and am voting to close as dupe, but it's up to you and the community whether to agree/accept that. (The top answer is Community Wiki, so I hope others feel empowered to add strategies over time.) That said, remember that your test _is a consumer of your class_ such that _you should design your class to be testable_, and that your test should _test the interface and not the implementation_. – Jeff Bowman Nov 09 '16 at 23:03
  • @Fabio - sorry, I don't understand what you mean by "just peek one", or how I can "test that it assigned properly". But I'm also looking at your answer... – mike rodent Nov 09 '16 at 23:31
  • @Jeff Bowman - yes, "test interface not implementation" - but I really don't understand how this is reconcilable with the TDD directive "don't write a line of code without a test". Maybe your view is that that directive is too extreme...? – mike rodent Nov 09 '16 at 23:34
  • @mike Define the behavior you want, write the test that tests for that behavior, then implement the behavior. If you write the minimum amount of code to get the tests to pass, then you have tested every line of code you write, and _have let the test drive the development_, right? – Jeff Bowman Nov 09 '16 at 23:38
  • @Jeff Bowman... fine, yes. But to illustrate with an example from my current "learning-based" project: to create a Lucene `IndexWriter` you have to create a whole bunch of other objects beforehand: an `Analyzer`, an `FSDirectory` and an `IndexWriterConfig`. You also have to create a suitable directory structure. All that code **could** go in one method, `createIndexWriter()`. But should it? – mike rodent Nov 09 '16 at 23:45
  • 1
    @mike I'm starting to understand your question. Creating/wiring a set of objects and then interacting with it sounds like an [awful lot of responsibilities](https://en.wikipedia.org/wiki/Single_responsibility_principle) for one class, right? Separating them might be a good idea. But even if you have one `void createIndexWriter()` method, if the creation has any visible side effects, detect/test those. If there are only invisible side effects, detect/test those via public methods. If there are no side effects, delete the method. :) – Jeff Bowman Nov 09 '16 at 23:53
  • @JeffBowman thanks again... the side effects are indeed the assignments of these (private field) objects! But enough for today, my head's spinning! – mike rodent Nov 10 '16 at 00:33

1 Answers1

1

Simple example: you need to write a class Class1 with two methods Method1 and Method2 which return values provided by some Helper class.

  1. [Test] Start writing test for first method - it will not compile
  2. [Production] Create class Class1 and Method1 - compile ok and test fail
  3. [Production] In Method1 you create new instance of Helper and call some of this methods which return expected value - test pass
  4. [Test] Create another test case for same Method1 - test fail
  5. [Production] Make changes in Method1 to satisfy both test cases - tests are green
  6. [Production] Make some refactorings in Method1 if you see possibility - test must stay green
  7. [Test] Create test for Method2 which will update some public field Field1 of Class1 - will not compile
  8. [Production] Create method Method2 and field Field1 in Class1 - compile - ok, test fail
  9. [Production] Create new instance of Helper class in Method2, call it method and update Field1 with expected value - test pass
  10. [Test] Create another test case for Method2 - test fail
  11. [Production] Make changes in Method2 to satisfy both test cases - tests are green

Now you have implemented needed behavior and noticed that both Method1 and Method2 create instance of Helper class.

  1. [Production] So you decide to declare private field of type Helper and use it in both methods - all tests are failed - because your private field not instantiated.
  2. [Production] - you instantiate new instance in the constructor - all tests are green

Later if you decide to move creation of Helper class outside and pass it to the Class1 as constructor's parameter, you just change your production code and tests will show you does you break anything without changing tests.

Main point of my answer, that if you think like "I want write this line of code - How I can test it" - it will goes you in thats kind of situation you got right now.
Try to first think what you want to test and after writing the test think how you will implement it.

Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Thanks... very useful. I'm just wondering how you would go about the sort of thing I'm working on now: to create a Lucene `IndexWriter` you have to create a whole bunch of other objects beforehand: an `Analyzer`, an `FSDirectory` and an `IndexWriterConfig`. You also have to create a suitable directory structure. All that code **could** go in one method, `createIndexWriter()`. But should it? – mike rodent Nov 10 '16 at 08:07
  • @mikerodent, I think you can move creation of those objects to another class like `IndexWriterDependenciesFactory` which will provide all objects as public fields or `Create...` like methods. Then you can test this class easily. And pass this "factory" as constructor parameter to the `IndexWriter` – Fabio Nov 10 '16 at 08:15
  • thanks... your second more explicit comment here makes it much clearer what you (and Jeff) feel to be the right kind of approach. I myself was wondering whether Jeff meant some sort of "factory" strategy. Of course most "factory" classes seem to use static methods a lot... but as these are harder to test I suppose the best thing is to avoid them... – mike rodent Nov 11 '16 at 12:50
  • @mikerodent, static class not hard to test, the problem of static class, that you cannot replace them with mocking behavior when you write test for unit which used those static methods – Fabio Nov 11 '16 at 12:53