Alternative to @InjectMocks? (simple @Set?) #3011
Replies: 3 comments
-
TLDR; It's all about private fields and not to end up with something like |
Beta Was this translation helpful? Give feedback.
-
I only glanced over this as I am on my phone, but for complex cases, would this be clearer and easier to read than explicitly initialising the constructor? @ExtendWith(MockitoExtension.class)
class TestFoo {
Foo foo;
@Mock Bar bar;
@Mock Baz baz;
@Mock Bork bork;
@BeforeEach
void setUp() {
foo = new Foo(bar, baz, bork);
}
} Guess my main concern is whether there are cases that would make tests harder to maintain via more complicated wiring between components. The problem with annotations is you increasingly defer more and more sanity checking until runtime, so begin to miss out on existing checks that compilers and IDEs can already perform without needing specific support for Mockito static analysis. Example: a test using the snippet above would fail compilation if I misspelled a parameter. If I were using strings, it would only detect the typo once JUnit runs. Worth noting Spring provides a mechanism for mocking beans via injection in spring-test as well. |
Beta Was this translation helpful? Give feedback.
-
@ascopes I agree that when one is the maintainer of a class (and the injection framework allows to do so), it is better to create the corresponding constructors. I also acknowledge your mentioned concerns. I am talking about situations where you need to construct instances of classes as prerequisite for you test that either are not maintained by you or you have other strong reasons not to touch the existing code. The problem I have with |
Beta Was this translation helpful? Give feedback.
-
Dear Mockitoans,
I won't bother you how I came to this topic. However I dug a little deeper into
@InjectMocks
and how it works and found some conceptual issues.What additionally made me think is this comment on #1066
I strongly agree to this statement.
The issues
I hope you find the following words not too offensive. This is not my intention. I rather want you to rethink the chosen path.
Here is the list of issues I currently see:
This is somehow contradictory to the mocking functionality itself, where the Mockito user has fine grained control over what happens and when.
@Inject
(CDI) or@Autowire
(Spring) annotations or not.Although this might be seen as a feature, it actually depends on the use case, if this is desirable or not.
@InjectMocks
somehow lures the user with unfulfillable expectations IMHO.(causing for example issues like ArrayIndexOutOfBoundsException when using multiple generic types in combination with complex generics [5.3.1] #3006 and ArrayIndexOutOfBoundsException with Version 5.3.1 #3000).
@InjectMocks
(only with additional tricks)I have to admit, that
@InjectMocks
is suitable for simple cases, where it works well.The proposal
I propose to introduce a new (helper) annotation
@Set
, which allows to put (not necessarily mocked) values into private fields. It works very simple compared to@InjectMocks
.A short example:
The
@Set
annotation would then be processed duringMockitoAnnotations.openMocks(testInstance)
or the corresponding place when usingMockitoExtension
,MockitoJunitRunner
or alike.The parameters so far are:
field
: the name of the field in the target objectsource
: the name of the source object (@Mock
or@Spy
) to use. More on that right away.It could be optional and if omitted the strategy is to use the field name for source lookup.
If someone wants to use a source, that is neither a spy nor a mock, I could imagine to additionally introduce an annotation
@Alias
to define such sources. For example:Or in the more shorter form:
Postponed (manual) Processing
However there might come up the need to process
@Set
at a later point in time independently fromopenMocks
. Especially when mocked objects are needed in the chain of objects to build up for testing.Therefor I would suggest an additional parameter
automatic
(default value istrue
) and a corresponding methodMockitoAnnotations.doSetFields(testInstance)
to trigger processing of all@Set
annotations that have set automatic=false.For example:
I am not sure about the names yet (please add your suggestions), but I think you've got the point.
Primitive Types
When thinking it through, also setting primitive types might be appreciated like so:
There is still a lot of room for additional thoughts and ideas.
The question
Strictly spoken
@Set
has nothing to do with mocking. So the basic question is, if Mockito is willing to pack additional items into its code that simply supports writing testing code?If that's the case, I could prepare a corresponding PR.
About Constructor Injection
While elaborating I was tempted to suggest an additional...
... but I came to the conclusion, that it is better to leave construction for such cases up to the user similar to the example with
@Set(...automatic = false)
andMockitoAnnotations.doSetFields(...)
above.I am looking forward to your feedback! 😉
Beta Was this translation helpful? Give feedback.
All reactions