I have the following classes:
public class Bar {
private final Listener mListener;
public Bar(Listener listener) {
mListener = listener;
}
}
public class Foo {
private final int mIntField;
private final int Bar mBarObject;
public Foo(int intField) {
mIntField = intField;
mBarObject = new Bar(new Listener() { ... });
}
}
Currently, I am writing test for Foo
class and since Bar
is not injected, I am using mockConstruction
to force all instance of Bar
being created after mockConstruction
call to be mocked. But I want to capture the argument to that constructor (ie Listener). How can I do that?
CodePudding user response:
You don't need to capture the Listener
when you unit-testing on Foo
. Because you are using Mocking framework which isolating indirect dependencies from the test class.
Let's make a simple figure as example, if your actual dependency path is like below:
Foo -> Bar -> Listener -> Other -> Other2 -> ...
When you unit-testing on Foo
, using mocking framework to make a MockedBar
and eliminate the dependency path of Bar
. Then, the dependency path becomes very simple:
Foo -> MockedBar
As a result, you can unit-testing your Foo
easily.
In order to do that, just refactor your code slightly. First, add a package-private constructor in your Foo
:
public class Foo {
private final int mIntField;
private final Bar mBarObject;
public Foo(int intField) {
mIntField = intField;
mBarObject = new Bar(new Listener() { ... });
}
// package-private constructor, for test only.
Foo(int intField, Bar bar) {
mIntField = intField;
mBarObject = bar;
}
}
Second, initialize and inject the mockedBar
into Foo
by constructor-injection.
public class FooTest {
private Foo foo;
@Before
public void setup() {
int intField = 123;
Bar mockedBar = mock(Bar.class);
when(mockedBar.invokeListener()).thenReturn(something);
foo = new Foo(intField, mockedBar);
}
@Test
public void test_foo() {
// test your foo
}
}
That's all. Now, you don't need to use the magic function mockConstruction
anymore.