機能テストの実装.XUnitによるネット
この記事では、機能テストを実装する方法を学びます.XUnitを使ったネットAPI.
必要条件: Visual Studio 2022を使用します.ネット6 SDK プロジェクトのダウンロードhere
テストされたプロジェクトは、しばしばシステムの下でテストされます.
機能テストは、通常の手配、アクト、およびテストステップを含む一連のイベントに従います. SUSTのWebホストが設定されています. テストサーバークライアントは、アプリケーションに要求を送信するために作成されます. アレンジテストステップが実行されます:テストアプリケーションは、要求を準備します. ACTテストステップが実行されます.クライアントは要求を送信し、応答を受け取ります. assertテストステップは実行されます:実際の応答は予想された応答に基づいてパスまたは失敗として検証されます. テストの全てが実行されるまで、プロセスは続く. 試験結果を報告した. 開発を始めましょう.
次の構造を持つ必要があります.
テストフォルダーで、右クリックします.新しいプロジェクトを追加ストアという名前のXUnitテストプロジェクト.テストと選択します.ターゲットフレームワークとしてのNET 6.
リファレンスを追加する
次のパッケージをインストールします マイクロソフト.アスピネット.MVCテスト マイクロソフト.EntityFrameworkCore.記憶 ASP .NETコア6はWebアプリケーションを導入しました
インストア.「機能をテスト」プロジェクトは、CustomWebApplicationFactoryクラスを作成します.
sutのデータベースコンテキストは
サンプルアプリケーションは、データベースコンテキストのサービスディスクリプタを見つけ、ディスクリプタを使用してサービス登録を削除します.次に、工場は新しい
テストクラスはクラスフィクスチャインターフェイスを実装する
コントローラフォルダを作成します.
ProductsController Testクラスを作成し、BaseControllerTestから拡張します.
テストエクスプローラーでわかるように、すべてのテストがパスしました.
ソースコードを見つけることができますhere .
読書ありがとう
読んでくださってありがとうございます.この記事は面白くて、将来役に立つかもしれません.何か質問やアイデアを議論する必要がある場合は、一緒に知識を交換し、交換することができる喜びになります.
必要条件:
機能テストは、通常の手配、アクト、およびテストステップを含む一連のイベントに従います.
次の構造を持つ必要があります.
テストフォルダーで、右クリックします.新しいプロジェクトを追加ストアという名前のXUnitテストプロジェクト.テストと選択します.ターゲットフレームワークとしてのNET 6.
リファレンスを追加する
Store.SharedDatabaseSetup
and Store.WebApi
プロジェクト.次のパッケージをインストールします
Startup
クラス.試験するWebApplicationFactory
なしでStartup
クラス、ASP .NETコア6アプリは暗黙の定義を公開する必要がありますProgram
クラス.だから、店で.WebAPIプロジェクトを作成する必要がありますProgram
部分クラス宣言を使用したクラス公開...
public partial class Program { }
Webアプリケーションの変更を行った後、テストプロジェクトではProgram
クラス用WebApplicationFactory
.WebApplicationFactory<TEntryPoint>
は、関数テストのテストサーバを作成するために使用されます.TEntryPoint
SUTのエントリポイントクラスは、通常起動クラスです.インストア.「機能をテスト」プロジェクトは、CustomWebApplicationFactoryクラスを作成します.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Store.Infrastructure.Persistence.Contexts;
using Store.SharedDatabaseSetup;
using System;
using System.Linq;
namespace Store.FunctionalTests
{
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the app's StoreContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<StoreContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add StoreContext using an in-memory database for testing.
services.AddDbContext<StoreContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForFunctionalTesting");
});
// Get service provider.
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
var storeDbContext = scopedServices.GetRequiredService<StoreContext>();
storeDbContext.Database.EnsureCreated();
try
{
DatabaseSetup.SeedData(storeDbContext);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the Store database with test messages. Error: {ex.Message}");
}
}
});
}
public void CustomConfigureServices(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Get service provider.
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
var storeDbContext = scopedServices.GetRequiredService<StoreContext>();
try
{
DatabaseSetup.SeedData(storeDbContext);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the Store database with test messages. Error: {ex.Message}");
}
}
});
}
}
}
CustomWebApplicationFactory
から継承するWebApplicationFactory
オーバーライドConfigureWebHost
. The IWebHostBuilder
サービスコレクションの構成を許可しますConfigureServices
.sutのデータベースコンテキストは
Startup.ConfigureServices
メソッド.テストアプリケーションのbuilder.ConfigureServices
コールバックは、アプリケーションの後に実行されますStartup.ConfigureServices
コードが実行されます.サンプルアプリケーションは、データベースコンテキストのサービスディスクリプタを見つけ、ディスクリプタを使用してサービス登録を削除します.次に、工場は新しい
StoreContext
これは、テスト用のメモリデータベースを使用します.テストクラスはクラスフィクスチャインターフェイスを実装する
(IClassFixture)
クラスをテストするには、クラスのテストで共有オブジェクトインスタンスを提供します.コントローラフォルダを作成します.
using System.Net.Http;
using Xunit;
namespace Store.FunctionalTests.Controllers
{
public class BaseControllerTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly CustomWebApplicationFactory<Program> _factory;
public BaseControllerTests(CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
}
public HttpClient GetNewClient()
{
var newClient = _factory.WithWebHostBuilder(builder =>
{
_factory.CustomConfigureServices(builder);
}).CreateClient();
return newClient;
}
}
}
CustomConfigureServices
メソッドCustomWebApplicationFactory
クラスをクライアントをカスタマイズするために作成されましたWithWebHostBuilder
. 別のテストではデータベースの変更を行い、他のテストの前に実行することができます.ProductsController Testクラスを作成し、BaseControllerTestから拡張します.
using Newtonsoft.Json;
using Store.ApplicationCore.DTOs;
using Store.FunctionalTests.Models;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Store.FunctionalTests.Controllers
{
public class ProductsControllerTests : BaseControllerTests
{
public ProductsControllerTests(CustomWebApplicationFactory<Program> factory) : base(factory)
{
}
[Fact]
public async Task GetProducts_ReturnsAllRecords()
{
var client = this.GetNewClient();
var response = await client.GetAsync("/api/Products");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<IEnumerable<ProductResponse>>(stringResponse).ToList();
var statusCode = response.StatusCode.ToString();
Assert.Equal("OK", statusCode);
Assert.True(result.Count == 10);
}
[Fact]
public async Task GetProductById_ProductExists_ReturnsCorrectProduct()
{
var productId = 5;
var client = this.GetNewClient();
var response = await client.GetAsync($"/api/Products/{productId}");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<SingleProductResponse>(stringResponse);
var statusCode = response.StatusCode.ToString();
Assert.Equal("OK", statusCode);
Assert.Equal(productId, result.Id);
Assert.NotNull(result.Name);
Assert.True(result.Price > 0);
Assert.True(result.Stock > 0);
}
[Theory]
[InlineData(0)]
[InlineData(20)]
public async Task GetProductById_ProductDoesntExist_ReturnsNotFound(int productId)
{
var client = this.GetNewClient();
var response = await client.GetAsync($"/api/Products/{productId}");
var statusCode = response.StatusCode.ToString();
Assert.Equal("NotFound", statusCode);
}
[Fact]
public async Task PostProduct_ReturnsCreatedProduct()
{
var client = this.GetNewClient();
// Create product
var request = new CreateProductRequest
{
Description = "Description",
Name = "Test product",
Price = 25.3
};
var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response1 = await client.PostAsync("/api/Products", stringContent);
response1.EnsureSuccessStatusCode();
var stringResponse1 = await response1.Content.ReadAsStringAsync();
var createdProduct = JsonConvert.DeserializeObject<SingleProductResponse>(stringResponse1);
var statusCode1 = response1.StatusCode.ToString();
Assert.Equal("Created", statusCode1);
// Get created product
var response2 = await client.GetAsync($"/api/Products/{createdProduct.Id}");
response2.EnsureSuccessStatusCode();
var stringResponse2 = await response2.Content.ReadAsStringAsync();
var result2 = JsonConvert.DeserializeObject<SingleProductResponse>(stringResponse2);
var statusCode2 = response2.StatusCode.ToString();
Assert.Equal("OK", statusCode2);
Assert.Equal(createdProduct.Id, result2.Id);
Assert.Equal(createdProduct.Name, result2.Name);
Assert.Equal(createdProduct.Description, result2.Description);
Assert.Equal(createdProduct.Stock, result2.Stock);
}
[Fact]
public async Task PostProduct_InvalidData_ReturnsErrors()
{
var client = this.GetNewClient();
// Create product
var request = new CreateProductRequest
{
Description = "Description",
Name = null,
Price = 0
};
var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response = await client.PostAsync("/api/Products", stringContent);
var stringResponse = await response.Content.ReadAsStringAsync();
var badRequest = JsonConvert.DeserializeObject<BadRequestModel>(stringResponse);
var statusCode = response.StatusCode.ToString();
Assert.Equal("BadRequest", statusCode);
Assert.NotNull(badRequest.Title);
Assert.NotNull(badRequest.Errors);
Assert.Equal(2, badRequest.Errors.Count);
Assert.Contains(badRequest.Errors.Keys, k => k == "Name");
Assert.Contains(badRequest.Errors.Keys, k => k == "Price");
}
[Fact]
public async Task PutProduct_ReturnsUpdatedProduct()
{
var client = this.GetNewClient();
// Update product
var productId = 6;
var request = new UpdateProductRequest
{
Description = "Description",
Name = "Test product",
Price = 17.67,
Stock = 94
};
var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response1 = await client.PutAsync($"/api/Products/{productId}", stringContent);
response1.EnsureSuccessStatusCode();
var stringResponse1 = await response1.Content.ReadAsStringAsync();
var updatedProduct = JsonConvert.DeserializeObject<SingleProductResponse>(stringResponse1);
var statusCode1 = response1.StatusCode.ToString();
Assert.Equal("OK", statusCode1);
// Get updated product
var response2 = await client.GetAsync($"/api/Products/{updatedProduct.Id}");
response2.EnsureSuccessStatusCode();
var stringResponse2 = await response2.Content.ReadAsStringAsync();
var result2 = JsonConvert.DeserializeObject<SingleProductResponse>(stringResponse2);
var statusCode2 = response2.StatusCode.ToString();
Assert.Equal("OK", statusCode2);
Assert.Equal(updatedProduct.Id, result2.Id);
Assert.Equal(updatedProduct.Name, result2.Name);
Assert.Equal(updatedProduct.Description, result2.Description);
Assert.Equal(updatedProduct.Stock, result2.Stock);
}
[Fact]
public async Task DeleteProductById_ReturnsNoContent()
{
var client = this.GetNewClient();
var productId = 5;
// Delete product
var response1 = await client.DeleteAsync($"/api/Products/{productId}");
var statusCode1 = response1.StatusCode.ToString();
Assert.Equal("NoContent", statusCode1);
// Get deleted product
var response2 = await client.GetAsync($"/api/Products/{productId}");
var statusCode2 = response2.StatusCode.ToString();
Assert.Equal("NotFound", statusCode2);
}
}
}
ソリューションを右クリックし、「実行」をクリックします.テストエクスプローラーでわかるように、すべてのテストがパスしました.
ソースコードを見つけることができますhere .
読書ありがとう
読んでくださってありがとうございます.この記事は面白くて、将来役に立つかもしれません.何か質問やアイデアを議論する必要がある場合は、一緒に知識を交換し、交換することができる喜びになります.
Reference
この問題について(機能テストの実装.XUnitによるネット), 我々は、より多くの情報をここで見つけました https://dev.to/cristofima/implement-functional-tests-on-net-with-xunit-78テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol