Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task<Result<TimelineItemDTO>> Handle(GetTimelineItemByIdQuery reque
{
var timelineItem = await _repositoryWrapper.TimelineRepository
.GetFirstOrDefaultAsync(
predicate: ti => true,
predicate: ti => ti.Id == request.Id,
include: ti => ti
.Include(til => til.HistoricalContextTimelines)
.ThenInclude(x => x.HistoricalContext)!);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// <copyright file="GetAllTimelineItemsHandlerTests.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace Streetcode.XUnitTest.BLL.MediatR.Timeline.TimelineItem.GetAll
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.EntityFrameworkCore.Query;
using Moq;
using Streetcode.BLL.DTO.Timeline;
using Streetcode.BLL.Interfaces.Logging;
using Streetcode.BLL.MediatR.Timeline.TimelineItem.GetAll;
using Streetcode.DAL.Entities.Timeline;
using Streetcode.DAL.Enums;
using Streetcode.DAL.Repositories.Interfaces.Base;
using Streetcode.DAL.Repositories.Interfaces.Timeline;
using Xunit;

/// <summary>
/// Unit tests for GetAllTimelineItemsHandler.
/// </summary>
public class GetAllTimelineItemsHandlerTests
{
private readonly Mock<IRepositoryWrapper> repoWrapperMock;
private readonly Mock<ITimelineRepository> timelineRepoMock;
private readonly Mock<IMapper> mapperMock;
private readonly Mock<ILoggerService> loggerMock;

private readonly GetAllTimelineItemsHandler handler;

/// <summary>
/// Initializes a new instance of the <see cref="GetAllTimelineItemsHandlerTests"/> class.
/// </summary>
public GetAllTimelineItemsHandlerTests()
{
this.repoWrapperMock = new Mock<IRepositoryWrapper>();
this.timelineRepoMock = new Mock<ITimelineRepository>();
this.mapperMock = new Mock<IMapper>();
this.loggerMock = new Mock<ILoggerService>();

this.repoWrapperMock
.Setup(x => x.TimelineRepository)
.Returns(this.timelineRepoMock.Object);

this.handler = new GetAllTimelineItemsHandler(
this.repoWrapperMock.Object,
this.mapperMock.Object,
this.loggerMock.Object);
}

/// <summary>
/// Should return timeline item DTO collection when items exist.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
[Fact]
public async Task Handle_ShouldReturnTimelineItemDtos_WhenItemsExist()
{
var query = new GetAllTimelineItemsQuery();

var timelineItems = new List<TimelineItem>
{
new TimelineItem
{
Id = 1,
Title = "Test Title",
Description = "Description",
Date = new DateTime(2020, 01, 01),
DateViewPattern = DateViewPattern.Year,
HistoricalContextTimelines = new List<HistoricalContextTimeline>
{
new HistoricalContextTimeline
{
HistoricalContext = new HistoricalContext
{
Id = 1,
Title = "Historical Context 1",
},
},
},
},
};

var expectedDtos = new List<TimelineItemDTO>
{
new TimelineItemDTO
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you will use mapper itself, you can use Map() method here to ensure "expectedDtos" and an actual output of the handler to be the same

{
Id = 1,
Title = "Test Title",
Description = "Description",
Date = new DateTime(2020, 01, 01),
DateViewPattern = DateViewPattern.Year,
HistoricalContexts = new List<HistoricalContextDTO>
{
new HistoricalContextDTO
{
Id = 1,
Title = "Historical Context 1",
},
},
},
};

this.timelineRepoMock
.Setup(r => r.GetAllAsync(
null,
It.IsAny<Func<IQueryable<TimelineItem>, IIncludableQueryable<TimelineItem, object>>>()))
.ReturnsAsync(timelineItems);

this.mapperMock
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setup forces mapper to return "expectedDtos" regardless of input, meaning our test will always pass. Consider not to use mocked mapper, but mapper itself to fix this problem. Fix other tests, having the same problem.

.Setup(m => m.Map<IEnumerable<TimelineItemDTO>>(It.IsAny<IEnumerable<TimelineItem>>()))
.Returns(expectedDtos);

var result = await this.handler.Handle(query, CancellationToken.None);

Assert.True(result.IsSuccess);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest using fluent assertions and method BeEquivalentTo() to compare "result.Value" and "expectedDtos"

Assert.NotNull(result.Value);
Assert.Single(result.Value);

var item = result.Value.First();

Assert.Equal("Test Title", item.Title);
Assert.Equal(1, item.Id);
Assert.Equal("Description", item.Description);

this.mapperMock.Verify(
m => m.Map<IEnumerable<TimelineItemDTO>>(
It.IsAny<IEnumerable<TimelineItem>>()),
Times.Once);

this.timelineRepoMock.Verify(
r => r.GetAllAsync(
null,
It.IsAny<Func<IQueryable<TimelineItem>,
IIncludableQueryable<TimelineItem, object>>>()),
Times.Once);
}

/// <summary>
/// Should throw exception when repository fails.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
[Fact]
public async Task Handle_ShouldPropagateException_WhenRepositoryThrows()
{
var query = new GetAllTimelineItemsQuery();

this.timelineRepoMock
.Setup(r => r.GetAllAsync(
null,
It.IsAny<Func<IQueryable<TimelineItem>,
IIncludableQueryable<TimelineItem, object>>>()))
.ThrowsAsync(new Exception("Database failure"));

var exception = await Assert.ThrowsAsync<Exception>(() =>
this.handler.Handle(query, CancellationToken.None));

Assert.Equal("Database failure", exception.Message);

this.mapperMock.Verify(
m => m.Map<IEnumerable<TimelineItemDTO>>(
It.IsAny<IEnumerable<TimelineItem>>()),
Times.Never);

this.loggerMock.Verify(
l => l.LogError(
It.IsAny<object>(),
It.IsAny<string>()),
Times.Never);
}

/// <summary>
/// Should return empty collection when no timeline items exist.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
[Fact]
public async Task Handle_ShouldReturnEmptyCollection_WhenNoItemsExist()
{
var query = new GetAllTimelineItemsQuery();

this.timelineRepoMock
.Setup(r => r.GetAllAsync(
null,
It.IsAny<Func<IQueryable<TimelineItem>,
IIncludableQueryable<TimelineItem, object>>>()))
.ReturnsAsync(new List<TimelineItem>());

this.mapperMock
.Setup(m => m.Map<IEnumerable<TimelineItemDTO>>(
It.IsAny<IEnumerable<TimelineItem>>()))
.Returns(new List<TimelineItemDTO>());

var result = await this.handler.Handle(query, CancellationToken.None);

Assert.True(result.IsSuccess);
Assert.NotNull(result.Value);
Assert.Empty(result.Value);

this.mapperMock.Verify(
m => m.Map<IEnumerable<TimelineItemDTO>>(
It.IsAny<IEnumerable<TimelineItem>>()),
Times.Once);

this.timelineRepoMock.Verify(
r => r.GetAllAsync(
null,
It.IsAny<Func<IQueryable<TimelineItem>,
IIncludableQueryable<TimelineItem, object>>>()),
Times.Once);
}
}
}
Loading
Loading