Refactor to introduce nullable types and reduce compiler warnings (#801)

* Introduce nullable types to Core, Infrastructure and PublicApi projects

* Refactor unit tests

* Fixed failing tests

* Introduce Parameter object for CatalogItem update method

* Refactor CataloItemDetails param object

* Refactor following PR comments
This commit is contained in:
Philippe Vaillancourt
2022-10-04 16:57:40 +01:00
committed by GitHub
parent aa6305eab1
commit a72dd775ee
51 changed files with 168 additions and 256 deletions

View File

@@ -1,6 +1,4 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.eShopWeb.FunctionalTests.Web;
using Microsoft.eShopWeb.FunctionalTests.Web;
using Xunit;
namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages;

View File

@@ -49,7 +49,7 @@ public class GetByIdWithItemsAsync
//Act
var spec = new OrderWithItemsByIdSpec(secondOrderId);
var orderFromRepo = await _orderRepository.GetBySpecAsync(spec);
var orderFromRepo = await _orderRepository.FirstOrDefaultAsync(spec);
//Assert
Assert.Equal(secondOrderId, orderFromRepo.Id);

View File

@@ -1,55 +0,0 @@
using System;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Xunit;
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.CatalogItemTests;
public class UpdateDetails
{
private CatalogItem _testItem;
private int _validTypeId = 1;
private int _validBrandId = 2;
private string _validDescription = "test description";
private string _validName = "test name";
private decimal _validPrice = 1.23m;
private string _validUri = "/123";
public UpdateDetails()
{
_testItem = new CatalogItem(_validTypeId, _validBrandId, _validDescription, _validName, _validPrice, _validUri);
}
[Fact]
public void ThrowsArgumentExceptionGivenEmptyName()
{
string newValue = "";
Assert.Throws<ArgumentException>(() => _testItem.UpdateDetails(newValue, _validDescription, _validPrice));
}
[Fact]
public void ThrowsArgumentExceptionGivenEmptyDescription()
{
string newValue = "";
Assert.Throws<ArgumentException>(() => _testItem.UpdateDetails(_validName, newValue, _validPrice));
}
[Fact]
public void ThrowsArgumentNullExceptionGivenNullName()
{
Assert.Throws<ArgumentNullException>(() => _testItem.UpdateDetails(null, _validDescription, _validPrice));
}
[Fact]
public void ThrowsArgumentNullExceptionGivenNullDescription()
{
Assert.Throws<ArgumentNullException>(() => _testItem.UpdateDetails(_validName, null, _validPrice));
}
[Theory]
[InlineData(0)]
[InlineData(-1.23)]
public void ThrowsArgumentExceptionGivenNonPositivePrice(decimal newPrice)
{
Assert.Throws<ArgumentException>(() => _testItem.UpdateDetails(_validName, _validDescription, newPrice));
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions;
@@ -9,12 +6,22 @@ public class TestParent : IEquatable<TestParent>
{
public int Id { get; set; }
public string Name { get; set; }
public string? Name { get; set; }
public IEnumerable<TestChild> Children { get; set; }
public IEnumerable<TestChild>? Children { get; set; }
public bool Equals([AllowNull] TestParent other) =>
other?.Id == Id && other?.Name == Name &&
(other?.Children is null && Children is null ||
(other?.Children?.Zip(Children)?.All(t => t.First?.Equals(t.Second) ?? false) ?? false));
public bool Equals([AllowNull] TestParent other)
{
if (other?.Id == Id && other?.Name == Name)
{
if (Children is null)
{
return other?.Children is null;
}
return other?.Children?.Zip(Children).All(t => t.First?.Equals(t.Second) ?? false) ?? false;
}
return false;
}
}

View File

@@ -12,19 +12,20 @@ public class AddItemToBasket
{
private readonly string _buyerId = "Test buyerId";
private readonly Mock<IRepository<Basket>> _mockBasketRepo = new();
private readonly Mock<IAppLogger<BasketService>> _mockLogger = new();
[Fact]
public async Task InvokesBasketRepositoryGetBySpecAsyncOnce()
{
var basket = new Basket(_buyerId);
basket.AddItem(1, It.IsAny<decimal>(), It.IsAny<int>());
_mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default)).ReturnsAsync(basket);
_mockBasketRepo.Setup(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default)).ReturnsAsync(basket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m);
_mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default), Times.Once);
_mockBasketRepo.Verify(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default), Times.Once);
}
[Fact]
@@ -32,9 +33,9 @@ public class AddItemToBasket
{
var basket = new Basket(_buyerId);
basket.AddItem(1, It.IsAny<decimal>(), It.IsAny<int>());
_mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default)).ReturnsAsync(basket);
_mockBasketRepo.Setup(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default)).ReturnsAsync(basket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m);

View File

@@ -11,6 +11,7 @@ public class DeleteBasket
{
private readonly string _buyerId = "Test buyerId";
private readonly Mock<IRepository<Basket>> _mockBasketRepo = new();
private readonly Mock<IAppLogger<BasketService>> _mockLogger = new();
[Fact]
public async Task ShouldInvokeBasketRepositoryDeleteAsyncOnce()
@@ -20,7 +21,7 @@ public class DeleteBasket
basket.AddItem(2, It.IsAny<decimal>(), It.IsAny<int>());
_mockBasketRepo.Setup(x => x.GetByIdAsync(It.IsAny<int>(), default))
.ReturnsAsync(basket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.DeleteBasketAsync(It.IsAny<int>());

View File

@@ -1,34 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Services;
using Moq;
using Xunit;
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests;
public class SetQuantities
{
private readonly int _invalidId = -1;
private readonly Mock<IRepository<Basket>> _mockBasketRepo = new();
[Fact]
public async Task ThrowsGivenInvalidBasketId()
{
var basketService = new BasketService(_mockBasketRepo.Object, null);
await Assert.ThrowsAsync<BasketNotFoundException>(async () =>
await basketService.SetQuantities(_invalidId, new System.Collections.Generic.Dictionary<string, int>()));
}
[Fact]
public async Task ThrowsGivenNullQuantities()
{
var basketService = new BasketService(null, null);
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
await basketService.SetQuantities(123, null));
}
}

View File

@@ -16,34 +16,19 @@ public class TransferBasket
private readonly string _nonexistentUserBasketBuyerId = "newuser@microsoft.com";
private readonly string _existentUserBasketBuyerId = "testuser@microsoft.com";
private readonly Mock<IRepository<Basket>> _mockBasketRepo = new();
[Fact]
public async Task ThrowsGivenNullAnonymousId()
{
var basketService = new BasketService(null, null);
await Assert.ThrowsAsync<ArgumentNullException>(async () => await basketService.TransferBasketAsync(null, "steve"));
}
[Fact]
public async Task ThrowsGivenNullUserId()
{
var basketService = new BasketService(null, null);
await Assert.ThrowsAsync<ArgumentNullException>(async () => await basketService.TransferBasketAsync("abcdefg", null));
}
private readonly Mock<IAppLogger<BasketService>> _mockLogger = new();
[Fact]
public async Task InvokesBasketRepositoryFirstOrDefaultAsyncOnceIfAnonymousBasketNotExists()
{
var anonymousBasket = null as Basket;
var userBasket = new Basket(_existentUserBasketBuyerId);
_mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default))
_mockBasketRepo.SetupSequence(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default))
.ReturnsAsync(anonymousBasket)
.ReturnsAsync(userBasket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId);
_mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default), Times.Once);
_mockBasketRepo.Verify(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default), Times.Once);
}
[Fact]
@@ -55,10 +40,10 @@ public class TransferBasket
var userBasket = new Basket(_existentUserBasketBuyerId);
userBasket.AddItem(1, 10, 4);
userBasket.AddItem(2, 99, 3);
_mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default))
_mockBasketRepo.SetupSequence(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default))
.ReturnsAsync(anonymousBasket)
.ReturnsAsync(userBasket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId);
_mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once);
Assert.Equal(3, userBasket.Items.Count);
@@ -72,10 +57,10 @@ public class TransferBasket
{
var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId);
var userBasket = new Basket(_existentUserBasketBuyerId);
_mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default))
_mockBasketRepo.SetupSequence(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default))
.ReturnsAsync(anonymousBasket)
.ReturnsAsync(userBasket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId);
_mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once);
_mockBasketRepo.Verify(x => x.DeleteAsync(anonymousBasket, default), Times.Once);
@@ -86,10 +71,10 @@ public class TransferBasket
{
var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId);
var userBasket = null as Basket;
_mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default))
_mockBasketRepo.SetupSequence(x => x.FirstOrDefaultAsync(It.IsAny<BasketWithItemsSpecification>(), default))
.ReturnsAsync(anonymousBasket)
.ReturnsAsync(userBasket);
var basketService = new BasketService(_mockBasketRepo.Object, null);
var basketService = new BasketService(_mockBasketRepo.Object, _mockLogger.Object);
await basketService.TransferBasketAsync(_existentAnonymousBasketBuyerId, _nonexistentUserBasketBuyerId);
_mockBasketRepo.Verify(x => x.AddAsync(It.Is<Basket>(x => x.BuyerId == _nonexistentUserBasketBuyerId), default), Times.Once);
}

View File

@@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Xunit;
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications;
@@ -12,9 +10,7 @@ public class CatalogFilterPaginatedSpecification
{
var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, null, null);
var result = GetTestCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestCollection());
Assert.NotNull(result);
Assert.Equal(4, result.ToList().Count);
@@ -25,9 +21,7 @@ public class CatalogFilterPaginatedSpecification
{
var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, 1, 1);
var result = GetTestCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestCollection()).ToList();
Assert.NotNull(result);
Assert.Equal(2, result.ToList().Count);

View File

@@ -19,9 +19,7 @@ public class CatalogFilterSpecification
{
var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterSpecification(brandId, typeId);
var result = GetTestItemCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestItemCollection()).ToList();
Assert.Equal(expectedCount, result.Count());
}

View File

@@ -14,9 +14,7 @@ public class CatalogItemsSpecification
var catalogItemIds = new int[] { 1 };
var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds);
var result = GetTestCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestCollection()).ToList();
Assert.NotNull(result);
Assert.Single(result.ToList());
@@ -28,9 +26,7 @@ public class CatalogItemsSpecification
var catalogItemIds = new int[] { 1, 3 };
var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds);
var result = GetTestCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestCollection()).ToList();
Assert.NotNull(result);
Assert.Equal(2, result.ToList().Count);

View File

@@ -15,14 +15,12 @@ public class CustomerOrdersWithItemsSpecification
{
var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId);
var result = GetTestCollection()
.AsQueryable()
.FirstOrDefault(spec.WhereExpressions.FirstOrDefault().Filter);
var result = spec.Evaluate(GetTestCollection()).FirstOrDefault();
Assert.NotNull(result);
Assert.NotNull(result.OrderItems);
Assert.Equal(1, result.OrderItems.Count);
Assert.NotNull(result.OrderItems.FirstOrDefault().ItemOrdered);
Assert.NotNull(result.OrderItems.FirstOrDefault()?.ItemOrdered);
}
[Fact]
@@ -30,15 +28,12 @@ public class CustomerOrdersWithItemsSpecification
{
var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId);
var result = GetTestCollection()
.AsQueryable()
.Where(spec.WhereExpressions.FirstOrDefault().Filter)
.ToList();
var result = spec.Evaluate(GetTestCollection()).ToList();
Assert.NotNull(result);
Assert.Equal(2, result.Count);
Assert.Equal(1, result[0].OrderItems.Count);
Assert.NotNull(result[0].OrderItems.FirstOrDefault().ItemOrdered);
Assert.NotNull(result[0].OrderItems.FirstOrDefault()?.ItemOrdered);
Assert.Equal(2, result[1].OrderItems.Count);
Assert.NotNull(result[1].OrderItems.ToList()[0].ItemOrdered);
Assert.NotNull(result[1].OrderItems.ToList()[1].ItemOrdered);

View File

@@ -22,7 +22,7 @@ public class GetOrderDetails
Order order = new Order("buyerId", address, new List<OrderItem> { item });
_mockOrderRepository = new Mock<IReadRepository<Order>>();
_mockOrderRepository.Setup(x => x.GetBySpecAsync(It.IsAny<OrderWithItemsByIdSpec>(), default))
_mockOrderRepository.Setup(x => x.FirstOrDefaultAsync(It.IsAny<OrderWithItemsByIdSpec>(), default))
.ReturnsAsync(order);
}