调用 .NetCore 的 .Netcore Controller 的单元测试用例 API
Unit test cases of .Netcore Controller that calls .NetCore API
我想写一个.net core MVC控制器的单元测试用例。控制器调用.net核心API.
我可以模拟 IHttpHelper,但它 return 始终为空。
我有我的 IHttpHelper
public interface IHttpHelper
{
Task<HttpResponseMessage> GetAsync(string apiUrl);
Task<HttpResponseMessage> PutAsync(string apiUrl, HttpContent content);
Task<HttpResponseMessage> PostAsync(string apiUrl, HttpContent content);
Task<HttpResponseMessage> DeleteAsync(string apiUrl);
}
我的网站 API 代码是
public class ClientTransferController : Controller
{
private readonly CTUClientTransferProxy _searchProxy;
private readonly IClientBLL _clientBll;
public ClientTransferController(IConfiguration configuration) : this(configuration, new ClientBLL(configuration), new WcfServiceHelper())
{
_searchProxy = new CTUClientTransferProxy(configuration, new WcfServiceHelper());
}
public ClientTransferController(IConfiguration configuration, IClientBLL clientBll, IWcfServiceHelper wcfServiceHelper)
{
_clientBll = clientBll;
}
[Route("api/wcfctu/validatesitecodes")]
public async Task<clsCTUValidateSiteCodesResults> ValidateSiteCodes([FromForm]string sourceSiteCode, [FromForm]string targetSiteCode)
{
var result = await _searchProxy.ValidateSiteCodesAsync(new clsCTUValidateSiteCodesCriteria { SourceSiteCode = sourceSiteCode, TargetSiteCode = targetSiteCode });
return result;
}
}
我正在通过我的 MVC 控制器 API 在上面调用
public class FirmAdminController : Controller
{
private readonly IHttpHelper _httpHelper;
public FirmAdminController(IHttpHelper httpHelper)
{
_httpHelper = httpHelper;
}
public async Task<IActionResult> ValidateSiteCodes(SiteCodeInputsViewModel siteCodeInputs)
{
if (ModelState.IsValid)
{
var values = new Dictionary<string, string>
{
{"sourceSiteCode", siteCodeInputs.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputs.TargetSiteCode.Sitecode}
};
var content = new FormUrlEncodedContent(values);
var clientTransferValoidateSiteCodesApiUrl = $"api/wcfctu/validatesitecodes";
HttpResponseMessage response = await _httpHelper.PostAsync(clientTransferValoidateSiteCodesApiUrl, content);
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
return Ok(jsonData);
}
}
return Json(null);
}
}
我想为 ClientTransferController
的 ValidateSiteCodes
编写单元测试用例。
下面是我的测试用例
public class FirmAdminControllerTests
{
private FirmAdminController _controller;
private readonly Mock<IHttpHelper> _mockHttpHelper;
public FirmAdminControllerTests()
{
_mockHttpHelper = new Mock<IHttpHelper>();
_controller = new FirmAdminController(_mockHttpHelper.Object);
}
[Fact]
public void ValidateSiteCodes_IfValid()
{
//Arrange
var clientTransferValoidateSiteCodesApiUrl = "api/wcfctu/validatesitecodes";
SiteCodeInputsViewModel siteCodeInputsViewModel = new SiteCodeInputsViewModel
{
SourceSiteCode = new SiteCodeInput { Sitecode = "Bravouat" },
TargetSiteCode = new SiteCodeInput { Sitecode = "CUAT" }
};
var values = new Dictionary<string, string>
{
{"sourceSiteCode", siteCodeInputsViewModel.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputsViewModel.TargetSiteCode.Sitecode}
};
clsCTUValidateSiteCodesResults result1 = new clsCTUValidateSiteCodesResults
{
Success = true
};
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);
var myContent = JsonConvert.SerializeObject(result1);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = new HttpResponseMessage
{
Content = byteContent,
StatusCode = HttpStatusCode.OK
};
var content = new FormUrlEncodedContent(values);
_mockHttpHelper.Setup(c => c.PostAsync(clientTransferValoidateSiteCodesApiUrl, content))
.Returns(async () => { await Task.Yield();
return responseMessage;
});
//Act
var result = _controller.ValidateSiteCodes(siteCodeInputsViewModel);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
}
}
但是
_mockHttpHelper.Setup(c => c.PostAsync(clientTransferValoidateSiteCodesApiUrl, content))
.Returns(async () => { await Task.Yield();
return responseMessage;
});
不return HttpResponseMessage
。它 returns null
这就是我的测试用例失败的原因。
测试失败的原因有很多。
被测控制器正在混合异步和阻塞调用,例如 .Result
、
var jsonData = response.Content.ReadAsStringAsync().Result;
这会导致死锁
引用Async/Await - Best Practices in Asynchronous Programming
动作应该一直是异步的
public async Task<IActionResult> ValidateSiteCodes(SiteCodeInputsViewModel siteCodeInputs) {
if (ModelState.IsValid) {
var values = new Dictionary<string, string> {
{"sourceSiteCode", siteCodeInputs.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputs.TargetSiteCode.Sitecode}
};
var content = new FormUrlEncodedContent(values);
var clientTransferValoidateSiteCodesApiUrl = $"api/wcfctu/validatesitecodes";
HttpResponseMessage response = await _httpHelper.PostAsync(clientTransferValoidateSiteCodesApiUrl, content);
if (response.IsSuccessStatusCode) {
var jsonData = await response.Content.ReadAsStringAsync();
return Ok(jsonData);
}
}
return BadRequest(ModelState);
}
现在开始测试。 post 设置因预期参数和返回响应的方式过于复杂。
Moq 有一个 ReturnsAsync
允许模拟异步流程按预期完成。
_mockHttpHelper
.Setup(_ => _.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>()))
.ReturnsAsync(responseMessage);
为了保持这种异步,测试方法也应该是异步的。
[Fact]
public async Task ValidateSiteCodes_IfValid() {
//...
}
并且await正在测试的方法。
//Act
var result = await _controller.ValidateSiteCodes(siteCodeInputsViewModel);
我想写一个.net core MVC控制器的单元测试用例。控制器调用.net核心API.
我可以模拟 IHttpHelper,但它 return 始终为空。 我有我的 IHttpHelper
public interface IHttpHelper
{
Task<HttpResponseMessage> GetAsync(string apiUrl);
Task<HttpResponseMessage> PutAsync(string apiUrl, HttpContent content);
Task<HttpResponseMessage> PostAsync(string apiUrl, HttpContent content);
Task<HttpResponseMessage> DeleteAsync(string apiUrl);
}
我的网站 API 代码是
public class ClientTransferController : Controller
{
private readonly CTUClientTransferProxy _searchProxy;
private readonly IClientBLL _clientBll;
public ClientTransferController(IConfiguration configuration) : this(configuration, new ClientBLL(configuration), new WcfServiceHelper())
{
_searchProxy = new CTUClientTransferProxy(configuration, new WcfServiceHelper());
}
public ClientTransferController(IConfiguration configuration, IClientBLL clientBll, IWcfServiceHelper wcfServiceHelper)
{
_clientBll = clientBll;
}
[Route("api/wcfctu/validatesitecodes")]
public async Task<clsCTUValidateSiteCodesResults> ValidateSiteCodes([FromForm]string sourceSiteCode, [FromForm]string targetSiteCode)
{
var result = await _searchProxy.ValidateSiteCodesAsync(new clsCTUValidateSiteCodesCriteria { SourceSiteCode = sourceSiteCode, TargetSiteCode = targetSiteCode });
return result;
}
}
我正在通过我的 MVC 控制器 API 在上面调用
public class FirmAdminController : Controller
{
private readonly IHttpHelper _httpHelper;
public FirmAdminController(IHttpHelper httpHelper)
{
_httpHelper = httpHelper;
}
public async Task<IActionResult> ValidateSiteCodes(SiteCodeInputsViewModel siteCodeInputs)
{
if (ModelState.IsValid)
{
var values = new Dictionary<string, string>
{
{"sourceSiteCode", siteCodeInputs.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputs.TargetSiteCode.Sitecode}
};
var content = new FormUrlEncodedContent(values);
var clientTransferValoidateSiteCodesApiUrl = $"api/wcfctu/validatesitecodes";
HttpResponseMessage response = await _httpHelper.PostAsync(clientTransferValoidateSiteCodesApiUrl, content);
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
return Ok(jsonData);
}
}
return Json(null);
}
}
我想为 ClientTransferController
的 ValidateSiteCodes
编写单元测试用例。
下面是我的测试用例
public class FirmAdminControllerTests
{
private FirmAdminController _controller;
private readonly Mock<IHttpHelper> _mockHttpHelper;
public FirmAdminControllerTests()
{
_mockHttpHelper = new Mock<IHttpHelper>();
_controller = new FirmAdminController(_mockHttpHelper.Object);
}
[Fact]
public void ValidateSiteCodes_IfValid()
{
//Arrange
var clientTransferValoidateSiteCodesApiUrl = "api/wcfctu/validatesitecodes";
SiteCodeInputsViewModel siteCodeInputsViewModel = new SiteCodeInputsViewModel
{
SourceSiteCode = new SiteCodeInput { Sitecode = "Bravouat" },
TargetSiteCode = new SiteCodeInput { Sitecode = "CUAT" }
};
var values = new Dictionary<string, string>
{
{"sourceSiteCode", siteCodeInputsViewModel.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputsViewModel.TargetSiteCode.Sitecode}
};
clsCTUValidateSiteCodesResults result1 = new clsCTUValidateSiteCodesResults
{
Success = true
};
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);
var myContent = JsonConvert.SerializeObject(result1);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = new HttpResponseMessage
{
Content = byteContent,
StatusCode = HttpStatusCode.OK
};
var content = new FormUrlEncodedContent(values);
_mockHttpHelper.Setup(c => c.PostAsync(clientTransferValoidateSiteCodesApiUrl, content))
.Returns(async () => { await Task.Yield();
return responseMessage;
});
//Act
var result = _controller.ValidateSiteCodes(siteCodeInputsViewModel);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
}
}
但是
_mockHttpHelper.Setup(c => c.PostAsync(clientTransferValoidateSiteCodesApiUrl, content))
.Returns(async () => { await Task.Yield();
return responseMessage;
});
不return HttpResponseMessage
。它 returns null
这就是我的测试用例失败的原因。
测试失败的原因有很多。
被测控制器正在混合异步和阻塞调用,例如 .Result
、
var jsonData = response.Content.ReadAsStringAsync().Result;
这会导致死锁
引用Async/Await - Best Practices in Asynchronous Programming
动作应该一直是异步的
public async Task<IActionResult> ValidateSiteCodes(SiteCodeInputsViewModel siteCodeInputs) {
if (ModelState.IsValid) {
var values = new Dictionary<string, string> {
{"sourceSiteCode", siteCodeInputs.SourceSiteCode.Sitecode},
{"targetSiteCode", siteCodeInputs.TargetSiteCode.Sitecode}
};
var content = new FormUrlEncodedContent(values);
var clientTransferValoidateSiteCodesApiUrl = $"api/wcfctu/validatesitecodes";
HttpResponseMessage response = await _httpHelper.PostAsync(clientTransferValoidateSiteCodesApiUrl, content);
if (response.IsSuccessStatusCode) {
var jsonData = await response.Content.ReadAsStringAsync();
return Ok(jsonData);
}
}
return BadRequest(ModelState);
}
现在开始测试。 post 设置因预期参数和返回响应的方式过于复杂。
Moq 有一个 ReturnsAsync
允许模拟异步流程按预期完成。
_mockHttpHelper
.Setup(_ => _.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>()))
.ReturnsAsync(responseMessage);
为了保持这种异步,测试方法也应该是异步的。
[Fact]
public async Task ValidateSiteCodes_IfValid() {
//...
}
并且await正在测试的方法。
//Act
var result = await _controller.ValidateSiteCodes(siteCodeInputsViewModel);