零度框架 1.0 版本正式发布,共 184 项改进。

This commit is contained in:
administrator committed 2024-05-31 12:28:02 +08:00
1 parent c14a872b77
commit baf06cd88f
182 files changed
+2537 -1613

No files matched your search

@@ -1 +0,0 @@
global using Xunit;
@@ -1,21 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07" />
<PackageReference Include="xunit" Version="2.7.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\HelloShop.ProductService\HelloShop.ProductService.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>
@@ -0,0 +1,62 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using AutoMapper;
using HelloShop.ProductService.Controllers;
using HelloShop.ProductService.Entities.Products;
using HelloShop.ProductService.Models.Products;
using HelloShop.ProductService.Services;
using Microsoft.AspNetCore.Mvc;
using Moq;
namespace HelloShop.ProductService.UnitTests
{
public class MockProductsControllerTest
{
[Fact]
public async Task GetProductReturnsNotFoundIfNotExists()
{
// Arrange
Mock<IProductService> mock = new();
mock.Setup(m => m.FindAsync(It.Is<int>(id => id == 1), It.IsAny<CancellationToken>())).ReturnsAsync((Product?)null);
// Act
IMapper mapper = new Mapper(new MapperConfiguration(cfg => cfg.CreateMap<Product, ProductDetailsResponse>()));
MockProductsController mockProductsController = new(mock.Object, mapper);
ActionResult<ProductDetailsResponse> result = await mockProductsController.GetProduct(1);
//Assert
Assert.IsType<NotFoundResult>(result.Result);
}
[Fact]
public async Task GetAllReturnsPorductListItems()
{
// Arrange
Mock<IProductService> mock = new();
mock.Setup(m => m.GetAllAsync(It.IsAny<CancellationToken>())).ReturnsAsync([new Product { Id = 1, Name = "Product 1", Price = 10 }, new Product { Id = 2, Name = "Product 2", Price = 20 }]);
// Act
IMapper mapper = new Mapper(new MapperConfiguration(cfg => cfg.CreateMap<Product, ProductListItem>()));
MockProductsController mockProductsController = new(mock.Object, mapper);
ActionResult<IEnumerable<ProductListItem>> result = await mockProductsController.GetProducts();
//Assert
Assert.NotNull(result.Value);
Assert.Collection(result.Value, productListItem1 =>
{
Assert.Equal(1, productListItem1.Id);
Assert.Equal("Product 1", productListItem1.Name);
Assert.Equal(10, productListItem1.Price);
}, productListItem2 =>
{
Assert.Equal(2, productListItem2.Id);
Assert.Equal("Product 2", productListItem2.Name);
Assert.Equal(20, productListItem2.Price);
});
}
}
}
@@ -0,0 +1,61 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using AutoMapper;
using HelloShop.ProductService.AutoMapper;
using HelloShop.ProductService.Entities.Products;
using HelloShop.ProductService.Models.Products;
using HelloShop.ProductService.UnitTests.Utilities;
using Microsoft.AspNetCore.Mvc;
namespace HelloShop.ProductService.UnitTests
{
public class ProductsControllerTest
{
[Fact]
public async Task GetProductByIdReturnsProductDetailsResponse()
{
// Arrange
await using EntityFrameworks.ProductServiceDbContext dbContext = new FakeDbContextFactory().CreateDbContext();
await dbContext.AddAsync(new Product { Id = 1, Name = "Product 1", Price = 10 });
await dbContext.SaveChangesAsync();
IMapper mapper = new MapperConfiguration(configure => configure.CreateMap<Product, ProductDetailsResponse>()).CreateMapper();
ProductsController productsController = new(dbContext, mapper);
// Act
ActionResult<ProductDetailsResponse> result = await productsController.GetProduct(1);
//Assert
Assert.Equal(10, result.Value?.Price);
}
[Theory]
[InlineData("Product 1", 10)]
[InlineData("Product 2", 20)]
[InlineData("Product 3", 30)]
public async Task PostProductReturnsProductDetailsResponse(string productName, decimal price)
{
// Arrange
await using EntityFrameworks.ProductServiceDbContext dbContext = new FakeDbContextFactory().CreateDbContext();
IMapper mapper = new MapperConfiguration(configure => configure.AddProfile<ProductsMapConfiguration>()).CreateMapper();
ProductsController productsController = new(dbContext, mapper);
// Act
ActionResult<ProductDetailsResponse> createdAtActionResult = await productsController.PostProduct(new ProductCreateRequest { Name = productName, Price = price });
ProductDetailsResponse? result = (createdAtActionResult?.Result as CreatedAtActionResult)?.Value as ProductDetailsResponse;
//Assert
Assert.Multiple(() =>
{
Assert.Equal(price, result?.Price);
Assert.Equal(productName, result?.Name);
});
}
}
}
@@ -0,0 +1,29 @@
# Unit test bast practices
## Unit testing best practices with .NET Core and .NET Standard
https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
## Testing without your production database system
https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database
## Unit test controller logic in ASP.NET Core
https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database
## Unit and integration tests in Minimal API apps
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/test-min-api
## Integration tests in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests
## Use Moq with xUnit in .NET Core unit tests
https://github.com/devlooped/moq
## Test gRPC services in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/grpc/test-services
@@ -1,11 +0,0 @@
namespace HelloShop.ProductService.UnitTests
{
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
}
@@ -0,0 +1,18 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using HelloShop.ProductService.EntityFrameworks;
using Microsoft.EntityFrameworkCore;
namespace HelloShop.ProductService.UnitTests.Utilities
{
public class FakeDbContextFactory : IDbContextFactory<ProductServiceDbContext>
{
public ProductServiceDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<ProductServiceDbContext>().UseInMemoryDatabase($"InMemoryTestDb-{DateTimeOffset.UtcNow.ToFileTime()}").Options;
return new ProductServiceDbContext(options);
}
}
}