Reputation: 63
I'm trying to inject a spy into private field private Map<Integer, IPatron> patrons;
with no luck. The field does not belong to any constructors or have any setters or getters. I'm trying to avoid changes to source code if possible.
I can explicitly assign the spy if the field is set to public but from what I understand it should happen automatically.
package library;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import library.entities.IPatron;
import library.entities.Library;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) // allows unnecessary "stubbing"
class TestTing{
@Spy
private Map<Integer, IPatron> patrons;
@Spy
@InjectMocks
Library library;
@BeforeEach
void setUp(){
}
@Test
void getPatronList(){
library.getPatronList();
//someTest
patrons.size();
}
}
private Map<Integer, IPatron> patrons;
public Library() {
patrons = new HashMap<>();
}
@Override
public List<IPatron> getPatronList() {
return new ArrayList<IPatron>(patrons.values());
}
My understanding is that the @Spy private Map<Integer, IPatron> patrons;
will find the private Map<Integer, IPatron> patrons;
in the class under test and replace it.
Upvotes: 0
Views: 1308
Reputation: 2565
You hard code patrons dependency inside Library class. Don't use hardcoding.
We should ask for dependencies and not define them ourselves.
We ask for mandatory dependencies in constructor and for optional ones in setters.
Change signature of your constructor to the following:
public Library(Map<Integer, IPatron> patrons) {
this.patrons = patrons;
}
Also make patrons field final.
In this case Library class doesn't care how patrons are created and what it is. Because it's not its responsibility.
You don't need any spy or mocks to test Library class now. And it's more extensible and testable.
Upvotes: 0
Reputation: 42441
First off, you're testing class Library
right, so its not a spy or anything, its just a regular instance, so that you should create it in test like this:
Library underTest = new Library(...);
Now, the answer to your question is not about Mockito, but about making the code unit-testable.
Not any produced code is automatically unit testable.
As class Library
is written now its impossible (unless you forcefully set the patrons with reflection). Reflection is also not a good way to do this.
I suggest using Dependency injection principle and refactor the code like this:
class Library {
private Map<Integer, IPatron> patrons;
public Library(Map<Integer, IPatron> patrons) {
this.patrons = patrons;
}
}
Then in test you can create patrons
as a Map, as a Spy, mock or whatever you want to mimic real interaction and inject into the Library
:
Library underTest = new Library(patrons);
Upvotes: 1