'What unit tests would be better or redudant
I started to learn a new topic for me - unit testing. Ffter writing a few tests, I began to wonder what else can be written and whether it will not be redundant. If you write code examples it would be even better for me, thank you.
For example do i need to write same Assert.ThrowsAsync<NullReferenceException>
test for method ChooseFilmToWatchWithChilds
or it will be redundant
/// <summary>
/// Check if database returns null
/// </summary>
[Fact]
public void NoFilmsInDatabase_ExceptionReturns()
{
Assert.ThrowsAsync<NullReferenceException>(
async () => { await _homeFilmHandler.IndicateFavouritesOfOurFilms(); });
}
Like this
/// <summary>
/// Check if database returns null
/// </summary>
[Fact]
public void NoFilmsInDatabaseChooseFilmToWatchWithChilds_ExceptionReturns()
{
Assert.ThrowsAsync<NullReferenceException>(
async () => { await _homeFilmHandler.ChooseFilmToWatchWithChilds(); });
}
Implementation class
using VideoArchive.Contracts.Abstractions.Data;
using VideoArchive.Contracts.Abstractions.FilmServices;
using VideoArchive.Contracts.Abstractions.Integrations;
using VideoArchive.Contracts.Models;
using VideoArchive.Contracts.Models.Home;
namespace VideoArchive.Implementations.FilmServices
{
public class HomeFilmHandler : IHomeFilmHandler
{
#region Constructors and DI
private readonly IFakeDatabaseService _fakeDatabaseService;
private readonly IFakeInegrationService _fakeInegrationService;
private readonly IMapper _mapper;
public HomeFilmHandler(
IFakeDatabaseService fakeDatabaseService,
IFakeInegrationService fakeInegrationService,
IMapper mapper)
{
_fakeDatabaseService = fakeDatabaseService;
_fakeInegrationService = fakeInegrationService;
_mapper = mapper;
}
#endregion
public async Task<HomeFilm> ChooseFilmToWatchWithChilds()
{
var allFilms = await _fakeDatabaseService.GetAllFilms();
var filmToWatch = allFilms
.Where(film => film.IsFavorite)
.OrderByDescending(film => film.TimesWatched);
foreach (var film in filmToWatch)
{
if (_fakeInegrationService.GetAgeRestrictions(_mapper.Map<Film>(film)) <= 6)
{
return film;
}
}
return await Task.FromResult<HomeFilm>(null);
}
public async Task IndicateFavouritesOfOurFilms()
{
var allFilms = await _fakeDatabaseService.GetAllFilms();
if (!allFilms.Any() || allFilms == null)
{
throw new NullReferenceException();
}
IndicateFilms(allFilms);
}
private async void IndicateFilms(List<HomeFilm> films)
{
foreach (var film in films)
{
if (film.TimesWatched >= 5)
{
film.IsFavorite = true;
await _fakeDatabaseService.UpdateFilm(film, film.Id);
}
}
}
}
}
TestsClass
using AutoMapper;
using VideoArchive.Contracts.Abstractions.Data;
using VideoArchive.Contracts.Abstractions.Integrations;
using VideoArchive.Contracts.Models.Home;
using VideoArchive.Implementations.FilmServices;
using Moq;
using Xunit;
namespace VideoArchive.Tests
{
public class HomeFilmHandlerTests
{
private readonly Fixture _fixture = new Fixture();
private readonly Mock<IFakeDatabaseService> _fakeDatabaseService = new Mock<IFakeDatabaseService>();
private readonly Mock<IFakeInegrationService> _fakeIntegrationService = new Mock<IFakeInegrationService>();
private readonly Mock<IMapper> _mapper = new Mock<IMapper>();
private readonly List<HomeFilm> _homeFilms;
private readonly HomeFilmHandler _homeFilmHandler;
public HomeFilmHandlerTests()
{
_homeFilms = _fixture.CreateMany<HomeFilm>().ToList();
_fakeDatabaseService
.Setup(service => service
.GetAllFilms())
.ReturnsAsync(_homeFilms);
_fakeDatabaseService
.Setup(service => service
.UpdateFilm(It.IsAny<HomeFilm>(), It.IsAny<int>()));
_fakeIntegrationService
.Setup(service => service
.GetAgeRestrictions(It.IsAny<HomeFilm>()));
_homeFilmHandler = new HomeFilmHandler(
_fakeDatabaseService.Object,
_fakeIntegrationService.Object,
_mapper.Object);
}
[Fact]
public void NoFilmsInDatabase_ExceptionReturns()
{
Assert.ThrowsAsync<NullReferenceException>(async () => { await _homeFilmHandler.IndicateFavouritesOfOurFilms(); });
}
[Fact]
public async void FilmsAreIndicatedRightTimes_UpdateFilmCalled()
{
var filmCountThatNeededToIndicate =
_homeFilms.Where(film => film.TimesWatched >= 5).Count();
await _homeFilmHandler.IndicateFavouritesOfOurFilms();
_fakeDatabaseService.Verify(
service => service.UpdateFilm(It.IsAny<HomeFilm>(), It.IsAny<int>()),
Times.Exactly(filmCountThatNeededToIndicate));
}
[Fact]
public void FilmWithChild()
{
//Write test here
}
}
}
FakeDatabaseService
using AutoFixture;
using VideoArchive.Contracts.Abstractions.Data;
using VideoArchive.Contracts.Models.Home;
namespace VideoArchive.Implementations.Data.Fake
{
/// <summary>
/// Fake service imitating databaseService
/// </summary>
public class FakeDatabaseService : IFakeDatabaseService
{
private readonly Fixture Fixture = new Fixture();
public async Task<List<HomeFilm>> GetAllFilms()
{
return Fixture.CreateMany<HomeFilm>().ToList();
}
public async Task<HomeFilm> GetFilmById(int id)
{
return Fixture.Build<HomeFilm>().With(film => film.Id, id).Create();
}
public async Task UpdateFilm(HomeFilm film, int id)
{
// Logic to updating film
}
}
}
FakeInegrationService
namespace VideoArchive.Contracts.Abstractions.Integrations
{
public interface IFakeIntegrationService
{
public int GetAgeRestrictions(IFilm film);
}
}
Solution 1:[1]
Testing that both method throw some exception is not redundant. Each method has its own behavior. The fact that they behave similarly doesn't change that.
What to test next? I suggest you check Microsoft's Unit testing best practices for some insights.
However, if you feel that your tests are redundant, it could be a sign that your architecture can be improved.
Example:
If you change the constructor of HomeFilmHandler
so that it throws an ArgumentNullException
when its fakeDatabaseService
parameter is null, you could replace the 2 tests that checks the NullReferenceException
by 1 test that checks that the constructor of HomeFilmHandler
throws when the fakeDatabaseService
is null. This is just an example to illustrate the point.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Batesias |