I am completely new to Mockito and I have to write a test case for my REST Controller, but I am not sure where should I start. Any help would be really appreciated.I've updated my controller based on given suggestion.
Here's my controller:
#RestController
#RequestMapping("/api")
public class TestController {
#Autowired
TestService _testService;
#RequestMapping(value = "/getsearchDetailCourse", method = RequestMethod.GET)
public List<TestDto> getsearchDetailCourse(#RequestParam("courseName") String courseName,
#RequestParam("courseId") Long courseId) throws Exception {
return (List<TestDto>) _testService.searchDetailCourse(courseName, courseId);
}
}
My TestDto:
public class TestDto {
private String numberOfCourse;
private Long courseId;
public TestDto(){}
public TestDto(String numberOfCourse,Long courseId ){
super();
this.numberOfCourse = numberOfCourse;
this.courseId = courseId;
}
public String getNumberOfCourse() {
return numberOfCourse;
}
public void setNumberOfCourse(String numberOfCourse) {
this.numberOfCourse = numberOfCourse;
}
public Long getCourseId() {
return courseId;
}
public void setCourseId(Long courseId) {
this.courseId = courseId;
}
}
Here's my test:
#RunWith(SpringRunner.class)
#WebMvcTest(value = TestController.class, secure = false)
public class TestMethod {
#Autowired
private MockMvc mockMvc;
#MockBean
private TestService testService;
TestDto testDto = new testDto("Test",2744L);
#Test
public void retrieveDetailsForCourse() throws Exception {
Mockito.when(
testService.searchDetailCourse(Mockito.anyString(),
,Mockito.anyLong())).thenReturn(testDto);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/api/getsearchDetailCourse").accept(
MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "[{\"numberOfCourse\":\"Testing1\",\"courseId\":2744},{\"numberOfCourse\":\"Testing2\",\"courseId\":2744}]";
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
I want to test the controller, please help me correct the test case above.
Related
I have a spring boot REST API with a GET method that returns data available in a DB. I am attempting to write an integration test to test this API method. I have configured the test to use the H2 database. I am trying to add some mock data to the database before the test is executed and see if the API retrieves that data. Following is the code I have written so far.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:application-test.properties")
public class MetaControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ProvinceDAO provinceDAO;
#Transactional
#Before
public void addData () {
Province southern = getProvinceEntity("Southern", "දකුණ", "தென்");
provinceDAO.createEntity(southern);
System.out.println(provinceDAO.findAll(Province.class).size());
}
#Test
public void testGetProvinces() throws Exception {
MvcResult result = mvc.perform(get("/meta/provinces"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
However, when I run this code, I am getting an error saying "org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available"
I have also attempted using #MockBean instead of #Autowired to bind the provinceDAO. Even though this prevents the error, it does not persist the entity in the database.
How should I write my testcase to test my method here?
Update:
application-test.properties
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Entity -> Province.java
#Entity
#Table(name = "w4a_province")
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "province_name")
private String name;
#Column(name = "province_name_si")
private String nameSi;
#Column(name = "province_name_ta")
private String nameTa;
.
.
}
GenericDAO.java
#Repository
public class GenericDAO<T> implements IGenericDAO<T> {
#PersistenceContext
private EntityManager em;
#Override
public Session getCurrentSession() {
return this.em.unwrap(Session.class);
}
#Override
public T findByPrimaryKey(Class<T> clazz, Object primaryKey) {
return getCurrentSession().find(clazz, primaryKey);
}
#Override
public List<T> findAll(Class<T> clazz) {
DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
return criteria.getExecutableCriteria(getCurrentSession()).list();
}
#Override
public T createEntity(T entity) {
getCurrentSession().save(entity);
return entity;
}
ProvinceDAOImpl.java
#Repository
public class ProvinceDAOImpl extends GenericDAO<Province> implements ProvinceDAO {
}
MetaController.java
#RestController
#PreAuthorize("permitAll()")
public class MetaController {
private final MetaService metaService;
#Autowired
public MetaController(MetaService metService) {
this.metaService = metService;
}
#GetMapping("/meta/provinces")
public ResponseEntity<List<ProvinceDTO>> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving list of provinces.");
}
List<ProvinceDTO> provinces = metaService.getProvinces();
return ResponseEntity.ok(provinces);
}
}
MetaServiceImpl.java
#Service
#Transactional
public class MetaServiceImpl implements MetaService {
private final ProvinceDAO provinceDAO;
#Autowired
public MetaServiceImpl(ProvinceDAO provnceDAO) {
this.provinceDAO = provnceDAO;
}
public List<ProvinceDTO> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Obtaining a list of provinces from database.");
}
List<Province> entities = provinceDAO.findAll(Province.class);
if (logger.isDebugEnabled()) {
logger.debug("Converting province entities to dtos.");
}
List<ProvinceDTO> dtos = new ArrayList<>();
for (int i = 0; i < entities.size(); i++) {
Province entity = entities.get(i);
if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.SINHALA_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameSi()));
} else if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.TAMIL_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameTa()));
} else {
dtos.add(new ProvinceDTO(entity.getId(), entity.getName()));
}
}
return dtos;
}
}
I managed to feed the database with the required data by placing a SQL script data-h2.sql with insert queries at the test/resources folder. This prevented the requirement to use an EntityManager or a DAO.
Furthermore, I added the following property to the application-test.properties file.
spring.datasource.platform=h2
In Order to test Rest Api You can try functional test as well as integration test.
You can prepare your own response formate as required and check whether the same is returned or else you can also verify whether the data from db is fine or not.Plz check the below example
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = FactsMain.class)
#WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
In First test case i am forming the response format and trying to return the same and then validating the same.Here i don't need the db data so i have kept service as mock instead of auto wired.And used ObjectMapper for converting json to java and then java obj to json from base Test class.
public class PersonalDetailsControllerTest extends BaseTest {
#MockBean
private IPersonalService service;
private static final String URI = "/api/personalDetails";
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void testGet() throws Exception {
PersonalDetailsEntity entity = new PersonalDetailsEntity();
List<PersonalDetailsEntity> dataList = new ArrayList<PersonalDetailsEntity>();
FactsAdminResponse<PersonalDetailsEntity> dataResponse = new FactsAdminResponse<PersonalDetailsEntity>();
entity.setId(1);
entity.setName(“Anthony Holmes”);
entity.setAge(26);
entity.setCity(“Banglore”);
entity.setCountry(“India”);
dataList.add(entity);
dataResponse.setData(dataList);
Mockito.when(service.getBuildings()).thenReturn(dataList);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI)
.accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
String expectedJson = this.mapToJson(dataResponse);
String outputInJson = mvcResult.getResponse().getContentAsString();
assertEquals(HttpStatus.OK.value(), response.getStatus());
assertEquals(expectedJson, outputInJson);
}
}
In below case we are getting the actual data in json format as we are doing rest api call and then just validating the status apart from status you can also cross check the data
public class PersonalDetailsControllerTest extends BaseTest {
private static final String URI = "/api/personalDetails";
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void getGet() throws Exception {
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(URL)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
//you got the content in string format now you can also validate the data
}
In my app, there is a controller, a service, a repo and a class. I am writing unit test to verify my PUT request. In postman, the put request works fine, however, when testing in JUnit test, it throws EmptyResultDataAccessException eror. Many other tests have the same problem and all of them require to find a specific entry in the repo by id. I think this is the problem. Please help me on this.
#Data
#Entity
public class ErrorMessage {
private #Id #GeneratedValue Long id;
private String message;
private int code;
public ErrorMessage() {
}
public ErrorMessage(int code, String message) {
this.code = code;
this.message = message;
}
}
#Repository
interface ErrorMessageRepository extends JpaRepository<ErrorMessage, Long> {
List<ErrorMessage> findByCode(int code);
}
#Service
public class ErrorMessageService {
#Autowired
ErrorMessageRepository repository;
#Transactional
public List<ErrorMessage> getAll()
{
return repository.findAll();
}
#Transactional(readOnly = true)
public Optional<ErrorMessage> getById(Long id)
{
return repository.findById(id);
}
#Transactional(readOnly = true)
public List<ErrorMessage> getByCode(int code)
{
return repository.findByCode(code);
}
#Transactional
public ErrorMessage saveOne(ErrorMessage messages)
{
return repository.save(messages);
}
#Transactional
public Optional<ErrorMessage> deleteById(long id)
{
Optional<ErrorMessage> em = repository.findById(id);
repository.deleteById(id);
return em;
}
#Transactional
public ErrorMessage updateById(long id, ErrorMessage newMessage)
{
ErrorMessage m = repository.findById(id).get();
m.setCode(newMessage.getCode());
m.setMessage(newMessage.getMessage());
repository.save(m);
return m;
}
}
class ErrorMessageController {
private static final Logger log = LoggerFactory.getLogger(ErrorMessageController.class);
#Autowired
ErrorMessageRepository repository;
#Autowired
private ErrorMessageService ems;
#GetMapping("/errormessages")
public List<ErrorMessage> getAll() {
return ems.getAll();
}
#GetMapping("/errormessagesbycode/{code}")
public List<ErrorMessage> getByCode(#PathVariable int code) {
return ems.getByCode(code);
}
#GetMapping("/errormessage/{id}")
ErrorMessage getById(#PathVariable Long id) {
return ems.getById(id)
.orElseThrow(() -> new MessageNotFoundException(id));
}
#PostMapping("/errormessage")
ErrorMessage newMessage(#RequestBody ErrorMessage newMessage) {
return ems.saveOne(newMessage);
}
#DeleteMapping("/errormessage/{id}")
Optional<ErrorMessage> deleteMessage(#PathVariable Long id) {
return ems.deleteById(id);
}
#PutMapping("/errormessage/{id}")
ErrorMessage updateMessage(#PathVariable Long id, #RequestBody ErrorMessage newMessage) {
return ems.updateById(id, newMessage);
}
}
#SpringBootTest
#AutoConfigureMockMvc
public class ErrorMessageTest {
private static ErrorMessage em, emId;
private static ObjectMapper mapper;
#Autowired
private MockMvc mockMvc;
#BeforeAll
public static void init() throws Exception {
mapper = new ObjectMapper();
em = new ErrorMessage(400, "bad request0");
emId = new ErrorMessage(400, "bad request0");
emId.setId(Long.valueOf(1));
}
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
}
Try this
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
ErrorMessageService errorMessageService = Mockito.mock(ErrorMessageService.class);
Mockito.when(errorMessageService.updateById(Mockito.any(), Mockito.any())).thenReturn(modifiedMessage);
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
I found out the bug. The order of the unit test is random. All i need to do is use #Order to ensure the order.
I am new to Junits and Mockito, I am writing a Unit test class to test my service class CourseService.java which is calling findAll() method of CourseRepository.class which implements CrudRepository<Topics,Long>
Service Class
#Service
public class CourseService {
#Autowired
CourseRepository courseRepository;
public void setCourseRepository(CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
public Boolean getAllTopics() {
ArrayList<Topics> topicList=(ArrayList<Topics>) courseRepository.findAll();
if(topicList.isEmpty())
{
return false;
}
return true;
}
}
Repository class
public interface CourseRepository extends CrudRepository<Topics,Long>{
}
Domain class
#Entity
#Table(name="Book")
public class Topics {
#Id
#Column(name="Topicid")
private long topicId;
#Column(name="Topictitle",nullable=false)
private String topicTitle;
#Column(name="Topicauthor",nullable=false)
private String topicAuthor;
public long getTopicId() {
return topicId;
}
public void setTopicId(long topicId) {
this.topicId = topicId;
}
public String getTopicTitle() {
return topicTitle;
}
public void setTopicTitle(String topicTitle) {
this.topicTitle = topicTitle;
}
public String getTopicAuthor() {
return topicAuthor;
}
public void setTopicAuthor(String topicAuthor) {
this.topicAuthor = topicAuthor;
}
public Topics(long topicId, String topicTitle, String topicAuthor) {
super();
this.topicId = topicId;
this.topicTitle = topicTitle;
this.topicAuthor = topicAuthor;
}
}
Following is the Junit class I have written but courseRepository is getting initialized to NULL and hence I am getting NullPointerException.
public class CourseServiceTest {
#Mock
private CourseRepository courseRepository;
#InjectMocks
private CourseService courseService;
Topics topics;
#Mock
private Iterable<Topics> topicsList;
#Before
public void setUp() {
MockitoAnnotations.initMocks(CourseServiceTest.class);
}
#Test
public void test_Get_Topic_Details() {
List<Topics> topics = new ArrayList<Topics>();
Mockito.when(courseRepository.findAll()).thenReturn(topics);
boolean result=courseService.getAllTopics();
assertTrue(result);
}
}
Change the setUp() method to:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
Probably you are dealing with some problem on the framework to make the mocked class be injected by the framework.
I recommend to use Constructor Injection, so you don't need to rely on the reflection and #Inject/#Mock annotations to make this work:
#Service
public class CourseService {
private final CourseRepository courseRepository;
// #Autowired annotation is optional when using constructor injection
CourseService (CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
// .... code
}
The test:
#Test
public void test_Get_Topic_Details() {
List<Topics> topics = new ArrayList<Topics>();
Mockito.when(courseRepository.findAll()).thenReturn(topics);
CourseService courseService = new CourseService(courseRepository);
boolean result = courseService.getAllTopics();
assertTrue(result);
}
I have the following code which attempts to save a POJO object (Actor) into MongoDB using Spring Mongo Repository, but the repository object is always Null. I have followed multiple examples but mainly this one
The POJO class:
#Document(collection = "actors")
public class Actor
{
#Id
private String id;
...
//constructor
//setters & getters
}
The repository:
public interface ActorRepository extends MongoRepository<Actor, String>
{
public Actor findByFNameAndLName(String fName, String lName);
public Actor findByFName (String fName);
public Actor findByLName(String lName);
}
The service that uses the repository:
#Service
public class ActorService
{
#Autowired
private ActorRepository actorRepository;
public Actor insert(Actor a)
{
a.setId(null);
return actorRepository.save(a);
}
}
And I access the service from a REST controller class:
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
private ActorService actorService = new ActorService();
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}
The error that I get is NullPointerException from this line: return actorRepository.save(a); in the ActorService.insert() method.
Any Idea why is this happening?
EDIT: Here is the Spring Configurations
#Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration
{
#Bean
public GridFsTemplate gridFsTemplate() throws Exception
{
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
#Override
protected String getDatabaseName()
{
return "SEaaS";
}
#Override
#Bean
public Mongo mongo() throws Exception
{
return new MongoClient("localhost" , 27017 );
}
public #Bean MongoTemplate mongoTemplate() throws Exception
{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
The problem is that you are not using Spring to get the ActorService dependency -instead you have manually instantiated the dependency using
private ActorService actorService = new ActorService();.
The following code is the easiest fix in order to inject the ActorService dependency into the controller.
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
#Autowired
private ActorService actorService;
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}
I'm having trouble setting session attributes for a test. I am using MockMvc to test calls to a controller. The session model has a member attribute on it (representing the person who has logged in). The SessionModel object is added as a session attribute. I was expecting it to be populated in the ModelMap parameter to formBacking method below, but the ModelMap is always empty.
The controller code works fine when running through the webapp, but not in the JUnit. Any idea what I could be doing wrong?
Here is my JUnit Test
#Test
public void testUnitCreatePostSuccess() throws Exception {
UnitCreateModel expected = new UnitCreateModel();
expected.reset();
expected.getUnit().setName("Bob");
SessionModel sm = new SessionModel();
sm.setMember(getDefaultMember());
this.mockMvc.perform(
post("/units/create")
.param("unit.name", "Bob")
.sessionAttr(SessionModel.KEY, sm))
.andExpect(status().isOk())
.andExpect(model().attribute("unitCreateModel", expected))
.andExpect(view().name("tiles.content.unit.create"));
}
and here is the controller in question
#Controller
#SessionAttributes({ SessionModel.KEY, UnitCreateModel.KEY })
#RequestMapping("/units")
public class UnitCreateController extends ABaseController {
private static final String CREATE = "tiles.content.unit.create";
#Autowired
private IUnitMemberService unitMemberService;
#Autowired
private IUnitService unitService;
#ModelAttribute
public void formBacking(ModelMap model) {
SessionModel instanceSessionModel = new SessionModel();
instanceSessionModel.retrieveOrCreate(model);
UnitCreateModel instanceModel = new UnitCreateModel();
instanceModel.retrieveOrCreate(model);
}
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String onCreate(
#ModelAttribute(UnitCreateModel.KEY) UnitCreateModel model,
#ModelAttribute(SessionModel.KEY) SessionModel sessionModel) {
model.reset();
return CREATE;
}
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String onCreatePost(
#ModelAttribute(SessionModel.KEY) SessionModel sessionModel,
#Valid #ModelAttribute(UnitCreateModel.KEY) UnitCreateModel model,
BindingResult result) throws ServiceRecoverableException {
if (result.hasErrors()){
return CREATE;
}
long memberId = sessionModel.getMember().getId();
long unitId = unitService.create(model.getUnit());
unitMemberService.addMemberToUnit(memberId, unitId, true);
return CREATE;
}
}
For your test class add the #WebAppConfiguration annotation and autowire the following as well.(WebApplicationContext and MockHttpSession )
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration({ "classpath:springDispatcher-servlet.xml" })
public class MySessionControllerTest {
#Autowired WebApplicationContext wac;
#Autowired MockHttpSession session;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void testUnitCreatePostSuccess() throws Exception {
UnitCreateModel expected = new UnitCreateModel();
expected.reset();
expected.getUnit().setName("Bob");
SessionModel sm = new SessionModel();
sm.setMember(getDefaultMember());
session.setAttribute(SessionModel.KEY, sm);
this.mockMvc.perform(
post("/units/create")
.session(session)
.param("unit.name", "Bob")
.andExpect(status().isOk())
.andExpect(model().attribute("unitCreateModel", expected))
.andExpect(view().name("tiles.content.unit.create"));
}
}