unit test for REST-API #PutMapping controller - problem with JSON path - unit-testing

I have problem with unit test for controller that has to update an employee in REST-API. For testing I use mockito, JUnit, hamcrest. I am only posting the most important parts of these classes.
controller method
#PutMapping("{id}")
public ResponseEntity<Employee> updateEmployee(#PathVariable(value = "id") Long id,
#RequestBody Employee employee) {
return new ResponseEntity<>(employeeService.UpdateEmployee(id, employee), HttpStatus.OK);
}
service methods
#Override
public Employee UpdateEmployee(Long employeeId, Employee employee) {
Employee employeeById = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee with id " + employeeId + " not found"));
employeeById.setFirstName(employee.getFirstName());
employeeById.setLastName(employee.getLastName());
employeeById.setEmail(employee.getEmail());
employeeRepository.save(employeeById);
return employeeById;
}
#Override
public Optional<Employee> getEmployeeById(Long id) {
return employeeRepository.findById(id);
}
unit test
#BeforeEach
public void setup() {
employee = Employee.builder()
.firstName(TEST_FIRST_NAME)
.lastName(TEST_LAST_NAME)
.email(TEST_EMAIL)
.build();
}
#Test
public void givenUpdatedEmployee_whenUpdateEmployee_thenReturnUpdateEmployeeObject() throws Exception {
//given
Long employeeId = 1L;
Employee employeeInfo = Employee.builder()
.firstName(NEW_TEST_FIRST_NAME)
.lastName(NEW_TEST_LAST_NAME)
.email(NEW_TEST_EMAIL)
.build();
BDDMockito.given(employeeService.getEmployeeById(employeeId))
.willReturn(Optional.of(employee));
BDDMockito.given(employeeService.UpdateEmployee(employeeId, employeeInfo))
.willAnswer((invocationOnMock -> invocationOnMock.getArgument(0)));
//when
ResultActions response = mockMvc.perform(put("/api/v1/employees/{id}", employeeId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(employeeInfo)));
//then
response.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.firstName", is(employeeInfo.getFirstName())))
.andExpect(jsonPath("$.lastName", is(employeeInfo.getLastName())))
.andExpect(jsonPath("$.email", is(employeeInfo.getEmail())));
}
receives an error
java.lang.AssertionError: No value at JSON path "$.firstName"

Related

How get token from headers to test my controller by mocking the service class

public class AdditionDto
{
public int HttpCode {get; set:}
public string UserName {get; set:}
public int Total {get; set:}
}
public interface IAddtionService
{
AdditionDto AddingTwoNumbers(int input1, int input2, string userName);
}
public class AdditionService : IAdditionService
{
public AdditionDto AddingTwoNumbers(int input1, int input2, string userName)
{
AdditionDto addDto = new AdditionDto()
{
httpCode = 200,
UserName = userName,
Total = input1 + input2
}
return addDto;
}
}
With a controller like this:
public class AdditionController : ControllerBase
{
private readonly IAddtionService _additionService;
public AdditionController(IAddtionService additionService)
{
_additionService = additionService;
}
[HttpGet]
[Authorize(Roles = "Admin")]
[Route("AddtionOfTwoNumbers")]
public IActionResult AddTwoNumbers(int input1, int input2)
{
if (ModelState.IsValid)
{
var token = HttpContext.Request.Headers["Authorization"].ToString();
var tokenbearer = token.Split(' ');
var handler = new JwtSecurityTokenHandler();
var decodedtoken = handler.ReadJwtToken(tokenbearer[1]);
string user = decodedtoken.Claims.Where(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name").FirstOrDefault().ToString();
var usr = user.Split(":");
string userName = usr[2].Trim();
var result = _additionService.AddingTwoNumbers(input1, input2,userName);
if (result.Httpcode == 200)
return Ok(result);
else
return StatusCode(500, result);
}
else
return BadRequest();
}
}
When i am trying to conduct xunit test on this api I am getting null at header level.
also I need to mock service method that is also not happening. mostly it is showing null exception even I have passed token in the headers also then I am facing mocking issue at the service method level. could you please help me to mock my service method to test that api through xunit framework....

How can i test that flexibleSearch works fine with .setCount()?

I have a java class that searches items, but in the method params this class receive a number of max items per query search (batchLines).
Class:
#Override
public List<OrderEntryItemModel> findOrderEntriesByStore(final BaseStoreModel store, final Date modifiedTime,
final int batchLines, final int start) {
final FlexibleSearchQuery query;
if (modifiedTime != null) {
query = new FlexibleSearchQuery(FIND_ORDER_ENTRY_ITEMS_BY_STORE_QUERY + MODIFIED_TIME_PARAM_QUERY);
query.addQueryParameter("modifiedtime", modifiedTime);
} else {
query = new FlexibleSearchQuery(FIND_ORDER_ENTRY_ITEMS_BY_STORE_QUERY);
}
query.setCount(batchLines);
query.setNeedTotal(true);
query.addQueryParameter("store", store);
query.setStart(start);
return getFlexibleSearchService().<OrderEntryItemModel>search(query).getResult();
}
So I have to test this class works fine with using .setCount(). I tried to do the test but always give me 3 and it must give me 1.
Test class:
#UnitTest
#RunWith(MockitoJUnitRunner.class)
public class DefaultLookerOrderEntryItemDaoTest {
private DefaultLookerOrderEntryItemDao lookerOrderEntryItemDao;
#Mock
private FlexibleSearchService flexibleSearchService;
#Mock
private SearchResult<OrderEntryItemModel> searchResult;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
lookerOrderEntryItemDao = new DefaultLookerOrderEntryItemDao(flexibleSearchService);
final OrderEntryItemModel oei1 = new OrderEntryItemModel();
final OrderEntryItemModel oei2 = new OrderEntryItemModel();
final OrderEntryItemModel oei3 = new OrderEntryItemModel();
final List<OrderEntryItemModel> orderEntryItems = new ArrayList<>();
orderEntryItems.add(oei1);
orderEntryItems.add(oei2);
orderEntryItems.add(oei3);
given(flexibleSearchService.<OrderEntryItemModel>search(isA(FlexibleSearchQuery.class))).willReturn(searchResult);
given(searchResult.getResult()).willReturn(orderEntryItems);
}
#Test
public void findOrderEntries(){
given(searchResult.getCount()).willReturn(1);
final List<OrderEntryItemModel> orderEntries = lookerOrderEntryItemDao.findOrderEntriesByStore(new BaseStoreModel(), null, 1, 0);
assertEquals(1, orderEntries.size());
}
}

Perform xUnit Moq test to create record and save it to DbContext

I am writing test for Web API application written in .NET CORE 3.1. I am using xUnit, AutoFixture & Moq for testing. I have a class that creates a new school instance in the database using Entity Framework/ DbContext. My question is how to mock dbContext & save changes, further my School DataModel has one: many relationships with SchoolBranch DataModel. I have followed this tutorial https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking
Error
Message:
Moq.MockException :
Expected invocation on the mock once, but was 0 times: m => m.Add<School>(It.IsAny<School>())
Performed invocations:
Mock<SchoolDbContext:1> (m):
No invocations performed.
Stack Trace:
Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)
Mock`1.Verify[TResult](Expression`1 expression, Times times)
CreateSchoolCommandTest.ExecuteMethod_ShouldReturnNewGuidId_IfSuccess() line 50
School
public class School
{
public School()
{
this.SchoolBranches = new HashSet<SchoolBranch>();
}
public Guid SchoolID { get; set; }
public string Name { get; set; }
public ICollection<SchoolBranch> SchoolBranches { get; set; }
}
SchoolBranch
public class SchoolBranch
{
public SchoolBranch()
{
}
public Guid SchoolBranchID { get; set; }
public Guid SchoolID { get; set; }
public string Address { get; set; }
public int PhoneNumber { get; set; }
public School School { get; set; }
}
CreateSchool Class
public class CreateSchool : BaseCommand<Guid>, ICreateSchool
{
public SchoolDto SchoolDtos { get; set; }
public CreateSchool(IAppAmbientState appAmbient) : base(appAmbient) { }
public override Guid Execute()
{
try
{
var schoolId = Guid.NewGuid();
List<SchoolBranch> schoolBranches = new List<SchoolBranch>();
foreach(var item in SchoolDtos.SchoolBranchDtos)
{
schoolBranches.Add(new SchoolBranch()
{
SchoolBranchID = Guid.NewGuid(),
SchoolID = schoolId,
Address = item.Address,
PhoneNumber = item.PhoneNumber
});
}
var school = new School()
{
SchoolID = schoolId,
Name = SchoolDtos.Name,
SchoolBranches = schoolBranches
};
schoolDbContext.Schools.Add(school);
schoolDbContext.SaveChanges();
return school.SchoolID;
}
catch(Exception exp)
{
appAmbientState.Logger.LogError(exp);
throw;
}
}
}
Test Class
public class CreateSchoolCommandTest
{
private readonly ICreateSchool sut;
private readonly Mock<IAppAmbientState> appAmbientState = new Mock<IAppAmbientState>();
[Fact]
public void ExecuteMethod_ShouldReturnNewGuidId_IfSuccess()
{
//Arrange
var fixture = new Fixture();
var schoolDtoMock = fixture.Create<SchoolDto>();
var schoolDbSetMock = new Mock<DbSet<School>>();
var schoolBranchDbSetMock = new Mock<DbSet<SchoolBranch>>();
var schoolDbContextMock = new Mock<SchoolDbContext>();
//schoolDbSetMock.Setup(x => x.Add(It.IsAny<School>())).Returns((School s) => s); // this also did not work
schoolDbContextMock.Setup(m => m.Schools).Returns(schoolDbSetMock.Object);
//Act
sut.SchoolDtos = schoolDtoMock;
var actualDataResult = sut.Execute();
// Assert
Assert.IsType<Guid>(actualDataResult);
schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
}
BaseCommand (DbContext is created here)
public abstract class BaseCommand<T>
{
protected SchoolDbContext schoolDbContext;
protected IAppAmbientState appAmbientState { get; }
public BaseCommand(IAppAmbientState ambientState)
{
this.schoolDbContext = new SchoolDbContext();
this.appAmbientState = ambientState;
}
public abstract T Execute();
}
For fix Error
You made just a little mistake. Insted of
schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
You should have
schoolDbSetMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
Because you use method Add() on schoolDbContext.Schools not on schoolDbContext
For injecting dbContext
Your BaseCoommand class constructor should look like this:
public BaseCommand(IAppAmbientState ambientState, SchoolDbContext schoolDbContext)
{
this.schoolDbContext = schoolDbContext;
this.appAmbientState = ambientState;
}
Your CreateSchool class constructor:
public CreateSchool(IAppAmbientState appAmbient, SchoolDbContext schoolDbContext) : base(appAmbient, schoolDbContext) { }
And next in test you should initialize CreateSchool in test like this:
var sut = new CreateSchool(ambientState, schoolDbContextMock.Object);
And it will work

WebTestClient does not post object

I am trying to perform simple test of reading method. I am trying to work with reactive approach so to test whole context I am using WebTestClient. Although post method seems to work correctly reading operation is not working. But why? Isn't it should be saved to embedded mongo database?
Do I need to wait for it somehow if this is reactive?
Exception is thrown definitely in service because I am seeing
com.geborskimateusz.util.exceptions.NotFoundException: No product found for productId: 1
This is how test looks like:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"spring.data.mongodb.port: 0"})
public class MovieServiceApplicationTests {
#Autowired
WebTestClient webTestClient;
#Autowired
MovieService movieService;
#Test
public void getMovie() {
Integer given = 1;
postAndVerify(given, HttpStatus.OK);
Movie movie = movieService.getMovie(given); **fails here**
assertNotNull(movie);
getAndVerify(given, HttpStatus.OK);
}
private WebTestClient.BodyContentSpec postAndVerify(Integer id, HttpStatus httpStatus) {
Movie movie = Movie.builder()
.movieId(id)
.title("Title for movie " + id)
.genre("Genre for movie " + id)
.address("Address for movie " + id)
.build();
return webTestClient.post()
.uri("/movie")
.body(Mono.just(movie), Movie.class)
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isEqualTo(httpStatus)
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody();
}
private WebTestClient.BodyContentSpec getAndVerify(Integer id, HttpStatus httpStatus) {
return webTestClient.get()
.uri("/movie/" + id)
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isEqualTo(httpStatus)
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.movieId").isEqualTo(id)
.jsonPath("$.genre").isNotEmpty()
.jsonPath("$.title").isNotEmpty()
.jsonPath("$.address").isNotEmpty();
}
}
Service and repository are very simple implementations:
Service:
#Slf4j
#RestController
public class BaseMovieService implements MovieService {
private final ServiceUtil serviceUtil;
private final MovieRepository movieRepository;
private final MovieMapper movieMapper = MovieMapper.INSTANCE;
#Autowired
public BaseMovieService(ServiceUtil serviceUtil, MovieRepository movieRepository) {
this.serviceUtil = serviceUtil;
this.movieRepository = movieRepository;
}
#Override
public Movie getMovie(Integer movieId) {
if (movieId < 1) throw new InvalidInputException("Invalid productId: " + movieId);
MovieEntity movieEntity = movieRepository.findMovieById(movieId)
.orElseThrow(() -> new NotFoundException("No product found for productId: " + movieId));
Movie movie = movieMapper.entityToApi(movieEntity);
movie.setAddress(serviceUtil.getServiceAddress());
log.debug("/movie return the found movie for movieId={}", movieId);
return movie;
}
#Override
public Movie createMovie(Movie movie) {
try {
MovieEntity movieEntity = movieMapper.apiToEntity(movie);
MovieEntity saved = movieRepository.save(movieEntity);
log.debug("createMovie: entity created for movieId: {}", movie.getMovieId());
return movieMapper.entityToApi(saved);
}catch (DuplicateKeyException e) {
throw new InvalidInputException("Duplicate key for movieId: " +movie.getMovieId());
}
}
#Override
public void deleteMovie(Integer movieId) {
}
}
And repo:
public interface MovieRepository extends PagingAndSortingRepository<MovieEntity, String> {
Optional<MovieEntity> findMovieById(Integer movieId);
}
This is however strange because I am sure that repo work fine, all tests below are passing:
#ExtendWith(SpringExtension.class)
#DataMongoTest
public class MovieRepositoryTest {
public static final int BASE_MOVIE_ID = 1;
#Autowired
MovieRepository movieRepository;
MovieEntity savedMovieEntity;
#BeforeEach
void setUp() {
movieRepository.deleteAll();
MovieEntity movieEntity = MovieEntity
.builder()
.movieId(BASE_MOVIE_ID)
.title("Raise of Jedi")
.address("123.321.54x24")
.genre("Sci-Fi")
.build();
savedMovieEntity = movieRepository.save(movieEntity);
assertEqualsMovie(movieEntity, savedMovieEntity);
}
#Test
void create() {
MovieEntity movieEntity = MovieEntity
.builder()
.movieId(2)
.title("Fall of Jedi")
.address("125.721.54x24")
.genre("Sci-Fi")
.build();
MovieEntity saved = movieRepository.save(movieEntity);
assertEqualsMovie(movieEntity, saved);
assertEquals(2, movieRepository.count());
}
#Test
void update() {
String givenTitle = "Updated Title";
savedMovieEntity.setTitle(givenTitle);
MovieEntity updated = movieRepository.save(savedMovieEntity);
assertEquals(givenTitle, updated.getTitle());
}
#Test
void findById() {
Optional<MovieEntity> optionalMovieEntity = movieRepository.findById(savedMovieEntity.getId());
assertTrue(optionalMovieEntity.isPresent());
MovieEntity movieEntity = optionalMovieEntity.get();
assertEqualsMovie(savedMovieEntity, movieEntity);
}
#Test
void shouldPerformOptimisticLocking() {
String concurrentM1actionData = "Concurrent action data performed on M1";
String concurrentM2actionData = "Concurrent action data performed on M2";
MovieEntity m1 = movieRepository.findById(savedMovieEntity.getId()).get();
MovieEntity m2 = movieRepository.findById(savedMovieEntity.getId()).get();
m1.setTitle(concurrentM1actionData);
// by updating Entity its version should be updated
movieRepository.save(m1);
// should fail because of version mismatch
try {
m2.setTitle(concurrentM2actionData);
movieRepository.save(m2);
fail("Expected an OptimisticLockingFailureException");
} catch (OptimisticLockingFailureException e) {
System.out.println("shouldPerformOptimisticLocking() -> catch OptimisticLockingFailureException");
}
//check current version and state
MovieEntity updatedMovieEntity = movieRepository.findById(savedMovieEntity.getId()).get();
assertEquals(1, (int) updatedMovieEntity.getVersion());
assertEquals(concurrentM1actionData, updatedMovieEntity.getTitle());
}
#Test
void delete() {
movieRepository.delete(savedMovieEntity);
assertEquals(0, movieRepository.count());
}
#Test
void duplicateMovieError() {
MovieEntity duplicate = MovieEntity.builder().build();
duplicate.setId(savedMovieEntity.getId());
assertThrows(DuplicateKeyException.class, () -> movieRepository.save(duplicate));
}
#Test
void paging() {
bulkSaveMovie();
Pageable nextPage = PageRequest.of(0, 4, Sort.Direction.ASC, "movieId");
nextPage = verifyPages(nextPage, "[1, 2, 3, 4]", true);
nextPage = verifyPages(nextPage, "[5, 6, 7, 8]", true);
verifyPages(nextPage, "[9, 10]", false);
}
private Pageable verifyPages(Pageable nextPage, String idsAsString, boolean hasNext) {
Page<MovieEntity> moviePage = movieRepository.findAll(nextPage);
assertEquals(hasNext, moviePage.hasNext());
String ids = moviePage.get().map(MovieEntity::getMovieId).collect(Collectors.toList()).toString();
assertEquals(idsAsString, ids);
return nextPage.next();
}
private void bulkSaveMovie() {
movieRepository.deleteAll();
List<MovieEntity> movies = IntStream.rangeClosed(1, 10)
.mapToObj(i -> MovieEntity.builder()
.movieId(i)
.title("Movie nr: " + i)
.build())
.collect(Collectors.toList());
movieRepository.saveAll(movies);
}
private void assertEqualsMovie(MovieEntity expected, MovieEntity actual) {
assertAll("Executing assertEqualsMovie(..)", () -> {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getVersion(), actual.getVersion());
assertEquals(expected.getMovieId(), actual.getMovieId());
assertEquals(expected.getTitle(), actual.getTitle());
assertEquals(expected.getAddress(), actual.getAddress());
assertEquals(expected.getGenre(), actual.getGenre());
});
}
}

Mocking OData service endpoint

I am trying to mock the OData service context using Moq to return a list of dummy entities so that I could base my unit test on that. I cannot expose my real model and application so I have created this simulated app and the portion, which I have exposed is similar.
MyOdataApplication consuming ODataEndpoint which I am testing.
public class MyApplication
{
private readonly IODataContext _odataContext;
public MyApplication(IODataContext odataContext){
_odataContext = odataContext;
}
public async Task<IEnumerable<Book>> GetBooks(string authorName)
{
IEnumerable<Book> books = null;
var query = (DataServiceQuery<Book>)_odataContext.Books.Where(x => x.Author = authorName);
books = await query.ExecuteAsync().ToList();
return books;
}
public bool async ValidateBooks(string authorName){
var books = await GetBooks(authorname);
//other code....
}
}
My Odata Service contract interface is
public interface IODataContext
{
global::Microsoft.OData.Client.DataServiceQuery<global::models.Book> Books { get; }
}
My Unit Test class is as follows.
[TestFixture]
public class MyTestClass
{
[Test]
public void TestOdataFunctionality()
{
var mockODataEndpoint = new Mock<IODataContext>();
//It fails here as its not able to convery IQueryable<Book> to DataServiceQuery<Book>
mockODataEndpoint.Setup(x => x.GetBooks(It.IsAny<string>)).Returns(GetDummyBooks());
var myApp = new MyApplication(mockODataEndpoint.Object);
//This is my main method which I need to test.
Task<bool> task = myApp.ValidateBooks("author name");
var isvalid = task.Result;
Assert.AreEqual(true, isvalid);
}
private DataServiceQuery<Book>GetDummyBooks()
{
var books = new List<Book>
{
new Book()
{
Name = "Book1",
Author = "author name",
//other properties...
}
};
//Not sure how to achieve this. The below line is giving error ???
return (DataServiceQuery<Book>)books.AsQueryable();
}
}
How do I mock the Odata Service endpoint so that I could test my ValidateBooks method?