How to mock model in service test? - java

I try write service test, for example, I have this ExamServiceImpl:
#Service
public class ExamServiceImpl implements ExamService {
#Autowired
private final SubjectService scoreService;
private final ScoreDAO scoreDAO;
#Autowired
public ExamServiceImpl(ScoreDAO scoreDAO) {
this.scoreDAO = scoreDAO;
}
#Override
public ResponseModel insertScore(RequestModel request) throws IOException {
SubjectModel subject = scoreService.getNameSubject(request);
ScoreModel score = new ScoreModel();
score.setStudentName(request.getStudentName);
score.setScore(request.getStudentScore);
score.setSubject(subject.getName);
int result = scoreDAO.insert(score);
return result;
}
}
Sample my test:
#SpringBootTest
public class ExamServiceImplTest {
#MockBean
private ScoreDAO scoreDAO;
#Autowired
private SubjectService subjectService;
#Autowired
private ExamService examService;
#Test
void insertScoreTest() {
SubjectModel resFromSubject = new SubjectModel();
resFromSubject.setSubject("Math");
Mockito.when(subjectService.getNameSubject(new RequestModel())).thenReturn(resFromSubject);
Mockito.when(scoreDAO.insert(new ScoreModel())).thenReturn(1);
int resultTest = examService.insertScore(new RequestModel());
assertSame(ex, 1);
}
But output resultTest is 0. I try debugger, I found mock scoreDAO.insert() return 0 >> is not working.
And I try like this:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
public class ExamServiceImplTest {
#Mock
private ScoreDAO scoreDAO;
#Mock
private SubjectService subjectService;
#InjectMocks
private ExamService examService = ExamServiceImpl(scoreDAO);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
void insertScoreTest() {
SubjectModel resFromSubject = new SubjectModel();
resFromSubject.setSubject("Math");
Mockito.when(subjectService.getNameSubject(new RequestModel())).thenReturn(resFromSubject);
Mockito.when(scoreDAO.insert(new ScoreModel())).thenReturn(1);
int resultTest = examService.insertScore(new RequestModel());
assertSame(ex, 1);
}
It's not work too.
Please, could you help write me test methods? I covered with tests more simple other services.
Thank you!

It doesn't work because new ScoreModel() inside Mockito.when() and inside ExamServiceImpl are two different objects. If you want scoreDAO to return 1 for every ScoreModel passed to it you can use:
Mockito.when(scoreDAO.insert(Mockito.any(ScoreModel.class)).thenReturn(1);

Related

How to inject value to a bean in spring test?

i have a question here, please give some ideas.
I have two beans. FaceComparisonServerImpl depends on FaceServer.
When i want to test. I want to change the String in my 'FaceServer' bean.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
#Autowired
private FaceServer faceServer;
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
...
}
}
#Component
public class FaceServer {
#Autowired
private RestTemplate restTemplate;
//Not final, just to test.
private String version = "1.0";
private static final String CODE = "code";
private static final String MESSAGE = "message";
//Final
private static final String SERVER_URL = "http://127.0.0.1:8066/api/ZKComparison";
}
Bellow is my test code.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
#Test
public void getServerInfo(){
//How can i modify the value of SERVER_URL in faceServer?
FaceComparsionInfo serverInfo = faceComparisonServer.getServerInfo();
System.out.println(serverInfo);
}
}
My question is:
How can i modified the value of 'version' and 'SERVER_URL' in #Bean(faceServer)?
Thanks you!
You need create FaceServer mock bean for test configuration.
And override required methods
#Configuration
Class TestConfig{
#Bean
#Primary
public FaceServer faceServer() {
return new FaceServer() {
#override
public String getServerInfo(){
return "required info";
}
};
}
}
The easiest way to customize the values is to make them Spring properties:
#Component
public class FaceServer {
#Value("${faceServer.version}")
private String version;
#Value("${faceServer.url}")
private String serverUrl;
// ...
}
You can either have default values for the #Value annotations or use some default property values in application.yml.
Now just override those properties in your test with the values you want:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(properties = {
"faceServer.version=1.0",
"faceServer.url=http://127.0.0.1:8066/api/ZKComparison"
})
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
// ...
}
However...
The second option is to make your classes more unit-testable. Prefer construction injection over field injection, and you can test your classes more independently.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
private final FaceServer faceServer;
public FaceComparisonServerImpl(FaceServer faceServer) {
this.faceServer = faceServer;
}
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
// ...
}
}
This now becomes unit-testable:
public class FaceServerTestByTyler {
private FaceComparisonServer faceComparisonServer;
private FaceServer faceServer;
#BeforeEach
public setup() {
faceServer = mock(FaceServer.class);
faceComparisonServer = new FaceComparisonServer(faceServer);
}
#Test
public void getServerInfo() {
when(faceServer.getServerInfo()).thenReturn(xxx);
// ...
}
}
The second option ends up with a test that runs much faster than any solutions that suggest to create a mock bean through a test configuration.

Mockito NullPointerException - Not recognizing repository

I'm having trouble figuring out why Mockito is throwing a NullPointerException when I'm telling the mock to return true.
Here is my JUnit Test:
public class PizzaValidatorTest {
private Pizza meatPizza;
private PizzaValidator validator = new PizzaValidator();
#MockBean
private IngredientRepository ingredientRepository;
#MockBean
private PizzaSizeRepository pizzaSizeRepository;
#Before
public void setUp() throws Exception {
meatPizza = new Pizza();
validator = new PizzaValidator();
}
#Test
public void validateValid() {
when(ingredientRepository.existsById(any())).thenReturn(true);
when(pizzaSizeRepository.existsById(any())).thenReturn(true);
assertTrue(validator.validate(meatPizza));
}
}
The PizzaValidator class is implemented below:
#Controller
public class PizzaValidator implements Validator<Pizza> {
#Autowired
IngredientRepository ingredientRepository;
#Autowired
PizzaSizeRepository pizzaSizeRepository;
#Override
public boolean validate(Pizza entity) {
return validatePizza(entity);
}
private boolean validatePizza(Pizza pizza) {
return validPizzaSize(pizza) && validIngredients(pizza);
}
private boolean validPizzaSize(Pizza pizza) {
return pizzaSizeRepository.existsById(pizza.getSizeDesc().getId());
}
private boolean validIngredients(Pizza pizza) {
for (Ingredient ingredient : pizza.getIngredients()) {
if (!ingredientRepository.existsById(ingredient.getId())) {
return false;
}
}
return true;
}
}
For some reason it seems like Mockito isn't connecting the mock repository with my class repository, but I can't figure out why. Any help is appreciated. Thanks.
You should not create the PizzaValidator using new keyword, you should #Autowire it in the test
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class PizzaValidatorTest {
private Pizza meatPizza;
#Autowire
private PizzaValidator validator;
#MockBean
private IngredientRepository ingredientRepository;
#MockBean
private PizzaSizeRepository pizzaSizeRepository;
#Before
public void setUp() throws Exception {
meatPizza = new Pizza();
}
#Test
public void validateValid() {
when(ingredientRepository.existsById(any())).thenReturn(true);
when(pizzaSizeRepository.existsById(any())).thenReturn(true);
assertTrue(validator.validate(meatPizza));
}
}

Q:Mockito - Using #Mock and #Autowired

I'd like to test a service class which has two other service classes like as below using Mockito.
#Service
public class GreetingService {
private final Hello1Service hello1Service;
private final Hello2Service hello2Service;
#Autowired
public GreetingService(Hello1Service hello1Service, Hello2Service hello2Service) {
this.hello1Service = hello1Service;
this.hello2Service = hello2Service;
}
public String greet(int i) {
return hello1Service.hello(i) + " " + hello2Service.hello(i);
}
}
#Service
public class Hello1Service {
public String hello(int i) {
if (i == 0) {
return "Hello1.";
}
return "Hello1 Hello1.";
}
}
#Service
public class Hello2Service {
public String hello(int i) {
if (i == 0) {
return "Hello2.";
}
return "Hello2 Hello2.";
}
}
I know how to mock Hello1Service.class and Hello2Service.class with Mockito like as below.
#RunWith(MockitoJUnitRunner.class)
public class GreetingServiceTest {
#InjectMocks
private GreetingService greetingService;
#Mock
private Hello1Service hello1Service;
#Mock
private Hello2Service hello2Service;
#Test
public void test() {
when(hello1Service.hello(anyInt())).thenReturn("Mock Hello1.");
when(hello2Service.hello(anyInt())).thenReturn("Mock Hello2.");
assertThat(greetingService.greet(0), is("Mock Hello1. Mock Hello2."));
}
}
I'd like to mock Hello1Service.class and inject Hello2Service.class using #Autowired like as below.
I tired to use #SpringBootTest but it did not work.
Is there a better way?
#RunWith(MockitoJUnitRunner.class)
public class GreetingServiceTest {
#InjectMocks
private GreetingService greetingService;
#Mock
private Hello1Service hello1Service;
#Autowired
private Hello2Service hello2Service;
#Test
public void test() {
when(hello1Service.hello(anyInt())).thenReturn("Mock Hello1.");
assertThat(greetingService.greet(0), is("Mock Hello1. Hello2."));
}
}
You want to inject dependency with some functionality to be formed then use #Spy.
You don't to load Spring Container and use #Autowired.
#Spy
private Hello2Service hello2Service=new Hello2Service();
You can read more detail about Mock vs Spy ;
https://dzone.com/articles/mockito-mock-vs-spy-in-spring-boot-tests
You can change with Spy for real object instead of Mock.
Test code will be like this;
#RunWith(MockitoJUnitRunner.class)
public class GreetingServiceTest {
#InjectMocks
private GreetingService greetingService;
#Mock
private Hello1Service hello1Service;
#Spy
private Hello2Service hello2Service;
#Test
public void test() {
when(hello1Service.hello(anyInt())).thenReturn("Mock Hello1.");
assertThat(greetingService.greet(0), is("Mock Hello1. Hello2."));
}
}

Unable to call the function from the mocked component class

I have a junit test method as follows:
#SpringBootTest
public class StoreIdAssignmentServiceTest {
private static final Logger log = LoggerFactory
.getLogger(StoreIdAssignmentServiceTest.class);
#InjectMocks
private StoreIdAssignmentService storeIdAssignmentService;
#Mock
private StoreIdAssignmentFactory storeIdAssignmentFactory;
#Mock
private DatabaseService databaseService;
#Test
public void rollUpFeed_Single_DealerAndStoreID_NoExisting() {
List<ScmsaPosTransRollup> scmsaPosTransRollupFeedList = new ArrayList<>();
ScmsaPosTransRollup posTransRollup = new ScmsaPosTransRollup();
posTransRollup.setJobLogId(8269726L);
posTransRollup.setDealerCode("3119255");
posTransRollup.setStoreId("9842");
posTransRollup.setTransactionDate(Timestamp
.valueOf("2018-03-01 13:00:00.00"));
posTransRollup.setQuantity(4);
posTransRollup.setRollupType("H");
scmsaPosTransRollupFeedList.add(posTransRollup);
Mockito.when(
databaseService.getUnProcessedRollUpFeedBasedonRollupType("H"))
.thenReturn(scmsaPosTransRollupFeedList);
List<PosHourlySt> existingPosHourlyStEntries = new ArrayList<>();
Mockito.when(databaseService.getDealerCodeFromPosHourly("3119255"))
.thenReturn(existingPosHourlyStEntries);
Mockito.when(databaseService.getDealerCodeFromPosHourly("3119255"))
.thenReturn(existingPosHourlyStEntries);
storeIdAssignmentService.processHourlyStateFeed();
assertNotNull(posHourlyStRepository.findAll());
}
}
And My StoreIdAssignmentService class will be:
#Service
public class StoreIdAssignmentService {
private StoreIdAssignmentFactory storeIdAssignmentFactory;
private DatabaseService databaseService;
#Autowired
public StoreIdAssignmentService(StoreIdAssignmentFactory storeIdAssignmentFactory,
DatabaseService databaseService) {
this.storeIdAssignmentFactory = storeIdAssignmentFactory;
this.databaseService = databaseService;
}
public void processHourlyStateFeed() {
.......................
calculateStateForPosHourlyStTransaction(posHourlyStToConsider, newPosHourlyStEntries);
.........
}
List<ScmsaPosTransRollup> scmsaPosTransRollupUpdatedFlagList = storeIdAssignmentFactory
.createUpdatedRollUpEntries(rollUpFeedByDealerCode);
saveAndUpdatePosHourlyStAndRollUpEntries(newPosHourlyStEntries, existingPosHourlyStEntries,
rollUpFeedByDealerCode, scmsaPosTransRollupUpdatedFlagList);
}
}
private Map<String, List<ScmsaPosTransRollup>> groupDealerCodeRollUpFeedByStoreId(
List<ScmsaPosTransRollup> rollUpFeedByDealerCode) {
// Grouping the rollUpFeedByDealerCode by storeID
return rollUpFeedByDealerCode.stream().collect(Collectors.groupingBy(ScmsaPosTransRollup::getStoreId));
}
private void calculateStateForPosHourlyStTransaction(ScmsaPosTransRollup scmsaPosTransRollupToConsider, List<PosHourlySt> newPosHourlyStEntries) {
List<PosHourlySt> posHourlyStList = newPosHourlyStEntries.stream().filter(
hourlyState -> (hourlyState.getStartDate().before(scmsaPosTransRollupToConsider.getTransactionDate())))
.collect(Collectors.toList());
..............
PosHourlySt posHourlySt=storeIdAssignmentFactory.createHourlyStEntryFromRollUp(scmsaPosTransRollupToConsider,
Timestamp.valueOf(scmsaPosTransRollupToConsider.getTransactionDate().toLocalDateTime().withHour(0).withMinute(0)),
Timestamp.valueOf(scmsaPosTransRollupToConsider.getTransactionDate().toLocalDateTime().withHour(23).withMinute(59)));
newPosHourlyStEntries.add(posHourlySt);
....................
}
}
and My Factory class would be:
#Component
public class StoreIdAssignmentFactory {
private static final Logger log = LoggerFactory.getLogger(StoreIdAssignmentFactory.class);
private ModelMapper modelMapper;
#Autowired
public StoreIdAssignmentFactory(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
public PosHourlySt createHourlyStEntryFromRollUp(ScmsaPosTransRollup scmsaPosTransRollup, Timestamp startDate, Timestamp endDate){
PosHourlySt posHourlySt = new PosHourlySt();
posHourlySt.setDealerCode(scmsaPosTransRollup.getDealerCode());
posHourlySt.setSourceJobLogId(scmsaPosTransRollup.getJobLogId());
posHourlySt.setStartDate(startDate);
posHourlySt.setStoreId(scmsaPosTransRollup.getStoreId());
posHourlySt.setEndDate(endDate);
posHourlySt.setJobLogId(0L);
posHourlySt.setSource("ROLLUP");
log.info("New Rec: {}", posHourlySt.toString());
return posHourlySt;
}
public PosHourlySt createHourlyStEntryFromPosHourlySt(PosHourlySt posHourlyStToSplit, Timestamp endDate){
PosHourlySt posHourlySt = new PosHourlySt();
posHourlySt.setDealerCode(posHourlyStToSplit.getDealerCode());
posHourlySt.setSourceJobLogId(posHourlyStToSplit.getJobLogId());
posHourlySt.setStartDate(posHourlyStToSplit.getStartDate());
posHourlySt.setStoreId(posHourlyStToSplit.getStoreId());
posHourlySt.setEndDate(endDate);
posHourlySt.setJobLogId(0L);
posHourlySt.setSource("ROLLUP");
log.info("SplitupRec: {}", posHourlySt.toString());
return posHourlySt;
}
public List<ScmsaPosTransRollup> createUpdatedRollUpEntries(List<ScmsaPosTransRollup> rollUpFeedByDealerCode) {
List<ScmsaPosTransRollup> scmsaPosTransRollupUpdatedFlagList = new ArrayList<>();
for(ScmsaPosTransRollup scmsaPosTransRollupFeed : rollUpFeedByDealerCode) {
ScmsaPosTransRollup scmsaPosTransRollupUpdateFlag = new ScmsaPosTransRollup();
modelMapper.map(scmsaPosTransRollupFeed, scmsaPosTransRollupUpdateFlag);
scmsaPosTransRollupUpdateFlag.setProcessedFlag("Y");
scmsaPosTransRollupUpdatedFlagList.add(scmsaPosTransRollupUpdateFlag);
}
return scmsaPosTransRollupUpdatedFlagList;
}
}
The StoreIdAssignmentService class contains the method "calculateStateForPosHourlyStTransaction" which calls some method in Factory class. When I debug as the junit test case , am not able to call that factory class method . What I am doing wrong here. Can anyone please suggest me.
You are mocking the factory:
#Mock
private StoreIdAssignmentFactory storeIdAssignmentFactory;
So you can't investigate the method createHourlyStEntryFromRollUp inside the factory, because the whole factory is mocked:
storeIdAssignmentFactory.createHourlyStEntryFromRollUp
If you trying to debug the createHourlyStEntryFromRollUp and the StoreIdAssignmentFactory is a #Component (or #Service), I recommend create a StoreIdAssignmentFactoryTest class test, use the #Autowired on it and #MockBean his dependencies.
Example:
#SpringBootTest
public class StoreIdAssignmentFactoryTest {
#Autowired
StoreIdAssignmentFactory factory;
#Test
public void testing() {
List<ScmsaPosTransRollup> list = factory.createHourlyStEntryFromRollUp(...);
//TODO asserts and etc
}
}
But this test is considered a integration test, because it load the whole spring context and beans related.
Another alternative is the (true) unit test. Use your constructor and not use #SpringBootTest, mock the dependencies (ModelMapper). This makes the test more fast and simple.
Example:
public class StoreIdAssignmentFactoryTest {
#Test
public void testing() {
ModelMapper mapper = mock(ModelMapper.class);
StoreIdAssignmentFactory factory = new StoreIdAssignmentFactory(mapper)
List<ScmsaPosTransRollup> list = factory.createHourlyStEntryFromRollUp();
//TODO asserts and etc
}
}

Failing to mock #Autowired Object

I am using Junit4 and Mockito for test cases, in the following code I am trying to mock a autowired object which throws null pointer exception inside the mocking class which means autowired object is not mocking properly
ContentDao.java
public class ContentDao {
#Autowired
private ConfigProperties configProperties;
public void fuction() {
int batchSize = configProperties.getBatchSize();
}
ConfigProperties.java
#ConfigurationProperties(ignoreUnknownFields = false, prefix = "cleanup")
public class ConfigProperties {
private int batchSize;
public int getBatchSize() {
return batchSize;
}
}
Trying to mock ConfigProperties.
#RunWith(MockitoJUnitRunner.class)
public class ContentDaoTest{
#InjectMocks
private ContentDao contentDao;
#Mock
private ConfigProperties configProperties;
#Test
public void functionTest(){
configProperties = mock(ConfigProperties.class);
when(configProperties.getBatchSize()).thenReturn(100);
ContentDao contentDao = new ContentDao();
contentDao.funtion();
}
funtion is called, but I get NPE in below line. Please help I am stuck here.
int batchSize = configProperties.getBatchSize();
If you set #Mock for configProperties, you should not mock again configProperties = mock(ConfigProperties.class);
In the same idea, as you set #InjectMocks for contentDao, you should not instantiate a new contentDao.
#RunWith(MockitoJUnitRunner.class)
public class ContentDaoTest {
#InjectMocks
private ContentDao contentDao;
#Mock
private ConfigProperties configProperties;
#Test
public void functionTest() {
Mockito.when(configProperties.getBatchSize()).thenReturn(100);
Assertions.assertThat(contentDao.getBatchSize()).isEqualTo(100);
}
}

Categories

Resources