Модульные тесты .Netcore Controller, который вызывает .NetCore API - PullRequest
0 голосов
/ 09 июня 2018

Я хочу написать пример модульного тестирования контроллера .net core MVC.Контроллер вызывает .net core API.

Я могу издеваться над IHttpHelper, но он всегда возвращает NULL.У меня есть 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;
    }
}

И я звоню через API выше через мой контроллер MVC

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);
    }
}

Я хочу написатьмодульные тесты для ValidateSiteCodes из ClientTransferController.

Ниже приведен мой тестовый пример

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;
                });

не возвращает HttpResponseMessage.Он возвращает null, и поэтому мой тестовый пример терпит неудачу.

1 Ответ

0 голосов
/ 09 июня 2018

Тест не выполняется по многим причинам.

Тестируемый контроллер смешивает асинхронные и блокирующие вызовы, такие как .Result,

var jsonData = response.Content.ReadAsStringAsync().Result;

, что может привести к взаимоблокировке

Ссылка Асинхронное / ожидание - Лучшие практики асинхронного программирования

Действие должно быть асинхронным на всем протяжении

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);
}

Теперь перейдем к тесту.Настройка поста слишком сложна из-за ожидаемых аргументов и способа возврата ответа.

У Moq есть ReturnsAsync, позволяющий завершить фиктивные асинхронные потоки, как и ожидалось.

_mockHttpHelper
    .Setup(_ => _.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>()))
    .ReturnsAsync(responseMessage);

При сохранении этой асинхронности метод тестирования также должен быть асинхронным.

[Fact]
public async Task ValidateSiteCodes_IfValid() {
    //...
}

и ждут тестируемого метода.

//Act
var result = await _controller.ValidateSiteCodes(siteCodeInputsViewModel);
...