C에서 NUnit 및 Moq를 사용하여 HttpClient에 대한 셀 테스트 작성
이 점을 더 잘 이해하기 위해 만든 프레젠테이션 프로젝트를 살펴보자.내 Github 페이지 here 에서 이 항목을 찾을 수 있습니다.
EmployeeApiClientService
라는 클래스를 만들었습니다. 이 클래스는 GetEmployeeAsync()
라는 방법이 있습니다. 이 방법은employeeid를 입력 매개 변수로employee API에 GET 요청을 보내고uri에employeeid를 포함해서 Employee
대상을 되돌려줍니다.public class EmployeeApiClientService
{
private readonly HttpClient employeeHttpClient;
public EmployeeApiClientService(HttpClient httpClient)
{
employeeHttpClient = httpClient;
}
//environment specific variables should always be set in a seperate config file or database.
//For the sake of this example I'm initialising them here.
public static string testDatabase = "SloughDB";
public static string environment = "TEST";
public async Task<Employee> GetEmployeeAsync(int employeeId)
{
Employee employee = null;
//Add headers
employeeHttpClient.DefaultRequestHeaders.Add("Accept", "application/json");
employeeHttpClient.DefaultRequestHeaders.Add("tracking-id", Guid.NewGuid().ToString());
//Conditional Headers
if (environment == "TEST")
{
employeeHttpClient.DefaultRequestHeaders.Add("test-db", testDatabase);
}
HttpResponseMessage response = await employeeHttpClient.GetAsync($"http://dummy.restapiexample.com/api/v1/employee/{employeeId}");
if (response.IsSuccessStatusCode)
{
employee = JsonConvert.DeserializeObject<Employee>(await response.Content.ReadAsStringAsync());
}
return employee;
}
}
일단 네가 클래스를 다 썼을 때, 너는 단원 테스트를 쓰기 시작하지만, 너는 끊겼다.HttpClient 클래스에 사용할 인터페이스가 없습니다Moq. 단원 테스트 기간에 실제 단점을 만나고 싶지 않습니다.여기서 뭐하는 거야?이 문제를 해결하는 방법에는 여러 가지가 있습니다.
방법 1: HttpClient 클래스에 대한 패키지 클래스 작성
이 방법은 HttpClientWrapper 같은 포장 클래스를 작성하고 포장 클래스에서 모든 HttpClient를 실현하는 방법을 작성한 다음에 실제 클래스에서 이 포장 클래스를 HttpClient가 아닌 의존항으로 사용해야 합니다.
그리고 단원 테스트에서 이 패키지 종류를 시뮬레이션하고 요청의 세부 사항을 검증할 수 있습니다.나에게 있어서 이런 방법은 작업량이 너무 많은 것 같을 뿐만 아니라, 간결한 실현도 아니다.
그래서 나는 주위를 둘러보니 HttpClient가 구조 함수를 다시 불러오고 HttpMessageHandler를 받아들이는 것을 발견했다.
public HttpClient(HttpMessageHandler handler);
이것이 바로 내가 두 번째 방법을 생각해 낸 이유다.방법 2: HttpMessageHandler를 시뮬레이션하여 HttpClient 클래스에 전달
HttpMessageHandler는 보호된 방법
SendAsync()
이 있는데 이것은 모든 HttpClient의GET/POST/PATCH/PUT 비동기적인 방법으로 호출되는 하부 방법이다.우리가 해야 할 일은 이 종류를 모의하고, 테스트 용례에 따라 SendAsync를 설정해서 우리가 원하는 값을 받아들이고 되돌려 주는 것이다.우리는 최소한의 주문량을 사용하여 이 목적을 달성할 수 있다.Moq는 에서 사용되는 아날로그 프레임입니다.NET에서 테스트할 셀을 베이스 종속 항목과 분리합니다.HttpMessageHandler에 대한 시뮬레이션을 만들고 이를 HttpClient의 재부팅 구조 함수에 전달할 수 있습니다.
이 점을 어떻게 실현하는지 봅시다.
아날로그 HttpMessageHandler
Moq를 사용하여 HttpMessageHandler의 아날로그 대상을 만들고 이를 HttpClient 클래스 구조 함수에 전달하며 테스트 설정에서 이 HttpClient 대상을 EmployeeApiClient 서비스 구조 함수에 전달합니다.
EmployeeApiClientService employeeApiClientService;
Mock<HttpMessageHandler> httpMessageHandlerMock;
[SetUp]
public void setUp()
{
httpMessageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
HttpClient httpClient = new HttpClient(httpMessageHandlerMock.Object);
employeeApiClientService = new EmployeeApiClientService(httpClient);
}
SendAsync 메서드 설정
Moq에서는 HttpMessageHandler 클래스에서 보호되어 클래스 밖에서 접근할 수 없기 때문에
SendAsync()
방법을 직접 설정할 수 없습니다.우리는 최소한의 주문량을 사용할 수 있다.보호된api는 시뮬레이션 대상에 대한 추가 방법을 제공합니다. 보호된 구성원의 이름을 사용하여 접근할 수 있습니다.
현재, 우리는 아날로그 HttpMessageHandler 대상의
.Protected()
방법을 설정하여, json 형식으로 Employee 대상이 있는 StatusCode 200을 되돌려줄 것입니다. 우리는 SendAsync()
방법을 사용하고, 단언 부분에서 이 방법에 대한 호출 횟수, 상세한 정보 요청 등을 검증할 수 있도록 ReturnsAsync()
방법을 사용할 것입니다.httpMessageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
).ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(JsonConvert.SerializeObject(new Employee()), Encoding.UTF8, "application/json")
}).Verifiable();
SendAsync 호출 확인
단원 테스트의 단언 부분에서 우리는 검증
Verify()
방법을 한 번만 호출할 것이다. 이것은 GET를 통해 호출을 요청한 것이다. 요청은 예상된 목표uri를 포함하고 요청은 모든 필수 헤더를 포함한다.httpMessageHandlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1),
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get
&& req.RequestUri.ToString() == targetUri // veryfy the RequestUri is as expected
&& req.Headers.GetValues("Accept").FirstOrDefault() == "application/json"
&& req.Headers.GetValues("tracking-id").FirstOrDefault() != null
&& environment.Equals("TEST") ?
req.Headers.GetValues("test-db").FirstOrDefault() == testDatabase :
req.Headers.GetValues("test-db").FirstOrDefault() == null
),
ItExpr.IsAny<CancellationToken>()
);
완전한 테스트 과정
테스트 용례를 포함하는 완전한 테스트 클래스는 요청을 정확하게 만들 수 있습니다. 아래와 같습니다.
[TestFixture]
class EmployeeApiClientServiceTests
{
EmployeeApiClientService employeeApiClientService;
Mock<HttpMessageHandler> httpMessageHandlerMock;
//environment specific variables should always be set in a separate config file or database.
//For the sake of this example I'm initialising them here.
string testDatabase = "SloughDB";
string environment = "TEST";
[SetUp]
public void setUp()
{
httpMessageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
HttpClient httpClient = new HttpClient(httpMessageHandlerMock.Object);
employeeApiClientService = new EmployeeApiClientService(httpClient);
}
[Test]
public async Task GivenICallGetEmployeeAsyncWithValidEmployeeId_ThenTheEmployeeApiIsCalledWithCorrectRequestHeadersAsync()
{
//Arrange
int employeeId = 1;
string targetUri = $"http://dummy.restapiexample.com/api/v1/employee/{employeeId}";
//Setup sendAsync method for HttpMessage Handler Mock
httpMessageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(JsonConvert.SerializeObject(new Employee()), Encoding.UTF8, "application/json")
})
.Verifiable();
//Act
var employee = await employeeApiClientService.GetEmployeeAsync(employeeId);
//Assert
Assert.IsInstanceOf<Employee>(employee);
httpMessageHandlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // verify number of times SendAsync is called
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // verify the HttpMethod for request is GET
&& req.RequestUri.ToString() == targetUri // veryfy the RequestUri is as expected
&& req.Headers.GetValues("Accept").FirstOrDefault() == "application/json" //Verify Accept header
&& req.Headers.GetValues("tracking-id").FirstOrDefault() != null //Verify tracking-id header is added
&& environment.Equals("TEST") ? req.Headers.GetValues("test-db").FirstOrDefault() == testDatabase : //Verify test-db header is added only for TEST environment
req.Headers.GetValues("test-db").FirstOrDefault() == null
),
ItExpr.IsAny<CancellationToken>()
);
}
}
따라서 HttpClient 호출을 테스트하기 위해 단원 테스트 용례를 쉽게 작성하고 요청의 각 방면을 검증하며 클래스가 응답을 처리하는 방법을 보았습니다.이 방법은 POST/PUT/PATCH 요청에도 사용할 수 있습니다. 왜냐하면 HttpClient의 모든 방법이 백엔드에서 HttpMessageHandler의 SendAsync()
방법을 사용하기 때문입니다.나는 네가 이것이 매우 재미있다고 느끼기를 바란다.읽어주셔서 감사합니다!
Reference
이 문제에 관하여(C에서 NUnit 및 Moq를 사용하여 HttpClient에 대한 셀 테스트 작성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/chaitanyasuvarna/writing-unit-tests-for-httpclient-using-nunit-and-moq-in-c-37jh텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)