Continuing to @daniu answer
You are stuck because of a mixed-match approach.
If you are writing test for a unit you will have to satisfy dependencies of the unit in your test also.
Let's consider you have a class A
with two dependencies B
and C
and your are injecting them old-fashion using setters like
public class A {
private B b;
private C c;
void method() {
System.out.println("A's method called");
b.method();
c.method();
}
public void setB(B b) {
this.b = b;
}
public void setC(C c) {
this.c = c;
}
}
Then unit test for above class will be like
public class ATest {
A a=new A();
@Test
public void test1() {
B b=new B();
C c=new C();
a.setB(b);
a.setC(c);
a.method();
}
}
dependencies satisfied in test also using setter injection. This was actually the way how java units were tested prior to mockito framework. Here B
and C
could have been test-doubles
or actual classes as per need.
But if we are using annotation based dependency injection in our classes using spring then our A
class will look something like
@Service
public class A {
@Inject
private B b;
@Inject
private C c;
void method() {
System.out.println("A's method called");
b.method();
c.method();
}
}
Here we are relying upon @Inject
to inject our dependency automatically using spring configurations.
But to unit test these we need some framework such as mockito which is equally capable of doing so i.e. injecting dependency without setter or constructor.
Every dependency can be either a mock
or a spy
(if actual invocation is needed)
Hence test of A
should look like
@RunWith(MockitoJUnitRunner.class)
public class ATest {
@InjectMocks
A a;
@Mock //or @Spy
B b;
@Mock //or @Spy
C c;
@Test
public void test() {
a.method();
}
}
But you can not mix and match. If you do not want to use @InjectMocks
and not want to @Spy
for every dependency that you want to be actually executed simply @Autowire
or instantiate A
in your class that will load A
with all the actual dependencies but then you will have to think how to override that one or two specific mocks that you want to be mocked.
1) You can provide a setter for that specific dependency and then you can create a mock and insert it.
public class ATest {
@Autowired
A a;
@Mock
B b;
@Test
public void test() {
a.setB(b);
a.method();
}
}
2) You can be a badass and use ReflectionTestUtils
to injected mocked dependencies on the fly even if your original class does not provide a setter for it.
@ContextConfiguration(classes=Config.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ATest {
@Autowired
A a;
@Mock
B b;
@Test
public void test() {
ReflectionTestUtils.setField(a, "b", b);
a.method();
}
}
3) Also as mentioned by @Yogesh Badke you can maintain such mocked beans in a separate test context file and use that but again that file will have to be maintained for every such specific example.