Skip to main content

Microsoft.AspNetCore.Mvc.Testing

WebApi 호출

curl -X POST --location "https://localhost:7230/people" \
-H "Content-Type: application/json" \
-d "{ \"FirstName\": \"Maarten\" }"

Invoke-WebRequest `
-Uri https://localhost:7230/people `
-Method Post `
-ContentType "application/json" `
-Body '{"FirstName": "Maarten"}'

WebApplicationFactory 생성 방법

테스트 단위

public class HelloControllerTests_Step1_ExplicitCreation
{
[Fact]
public async Task WebApi_GetGretting_ShouldBeTrue()
{
// Arrange
await using var application = new WebApplicationFactory<Program>();

// 1. CreateClient 메서드를 호출하면 Program 인스턴스를 생성합니다.
// 2. CreateClient N번 호출해도 Program 인스턴스는 1번만 생성합니다.
using var sut = application.CreateClient();
using var sut2 = application.CreateClient();

// Act
var actual = await sut.GetAsync("/api/greeting/Foo");

// Assert
actual.StatusCode.Should().Be(HttpStatusCode.OK);
}
}

클래스 1개 단위

public class HelloControllerTests_Step2_ClassFixture
: IClassFixture<WebApplicationFactory<Program>>
, IDisposable
{
readonly HttpClient _sut;

public HelloControllerTests_Step2_ClassFixture(WebApplicationFactory<Program> app)
{
_sut = app.CreateClient();
}

public void Dispose()
{
}

[Fact]
public async Task WebApi_GetGretting_ShouldBeTrue()
{
// Act
var actual = await _sut.GetAsync("/api/greeting/Foo");

// Assert
actual.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Fact]
public void Others()
{

}
}

클래스 1개 단위(자신 클래스)

public class ApiWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(config =>
{
var path = Directory.GetParent(Environment.CurrentDirectory)!.Parent!.Parent!.FullName;

IConfiguration configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddJsonFile(IntegrationTest.AppsettingsTestJson)
.Build();

er.AddJsonFile(Path.Combine(path, IntegrationTest.AppsettingsTestJson));

config.AddConfiguration(configuration);
});

// ConfigureServices 호출 후 ConfigureTestServices이 호출되어 DI을 재정의할 수 있습니다.
builder.ConfigureTestServices(services =>
{
services.AddTransient<IWeatherForecastConfigService, WeatherForecastConfigMock>();
});
}
}

public class HelloControllerTests_Step3_OwnClassFixture
: IClassFixture<ApiWebApplicationFactory>
{
readonly HttpClient _sut;

public HelloControllerTests_Step3_OwnClassFixture(ApiWebApplicationFactory application)
{
_sut = application.CreateClient();
}

[Fact]
public async Task WebApi_GetGretting_ShouldBeTrue()
{
// Act
var actual = await _sut.GetAsync("/api/greeting/Foo");

// Assert
actual.StatusCode.Should().Be(HttpStatusCode.OK);
}
}
  • dotnet test --filter Category = Integration
  • dotnet test --filter Category != Integration

클래스 N개 단위

// WebAppFactoryCollection          : Collection 사용
// WebAppFactoryCollectionFixture : CollectionDefinition 정의
// WebAppFactoryFixture : Fixture

[CollectionDefinition(CollectionName.WebAppFactoryCollection)]
public sealed class WebAppFactoryCollectionFixture
: ICollectionFixture<WebAppFactoryFixture>
{
}

public sealed class WebAppFactoryFixture
: WebApplicationFactory<Program>
, IAsyncLifetime
{
// ...
}

[Collection(CollectionName.WebAppFactoryCollection)]
public sealed class UserControllerTests
{
public UserControllerTests(WebAppFactoryFixture webAppFactory)
{
_sut = webAppFactory.CreateClient();
}

// ...
}

디버깅 환경

VS 디버깅일 때 Web Browser 활성화 비활성화

  • launchSettings.json
    {
    "profiles": {
    "Api": {
    "launchBrowser": false,
    }
    }
    }

REST Client VS 확장 도구

@hostname=localhost
@port=5168
@host = http://{{hostname}}:{{port}}
@contentType = application/json

### GET
GET {{host}}/api/students/1

### PUT & POST
PUT {{host}}/api/students/2
Content-Type:{{contentType}}

{
"name": "foo",
"address": "hello world 1004"
}