模拟IHttpClientFactory-xUnit C#

时间:2019-12-08 17:12:53

标签: c# httpclient xunit fixtures httpclientfactory

我试图在我的项目(带有.net core 2.1的c#)中构建通用HTTP服务,并且已经按照下面的代码段HttpService进行了构建。

我还开始通过从我的业务逻辑类中调用它来开始使用它,该逻辑类使用此通用PostAsync方法将HTTP调用发布到正文中包含内容的第三者。效果很好。

但是,当我尝试对其进行测试时,我失败了! 实际上,当我尝试调试(测试模式)时,即使使用伪造的对象和模拟,调试器到达业务类null中的这一行var result = await _httpService.PostAsync("https://test.com/api", content);时,我也会收到Processor响应,尽管它在调试模式,无需测试/模拟。

HTTP服务:

public interface IHttpService
{
    Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
}

public class HttpService : IHttpService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public HttpService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
    {
        var httpClient = _httpClientFactory.CreateClient();
        httpClient.Timeout = TimeSpan.FromSeconds(3);
        var response = await httpClient.PostAsync(requestUri, content).ConfigureAwait(false);
        response.EnsureSuccessStatusCode();

        return response;
    }
}

商务舱:

public class Processor : IProcessor
{
    private readonly IHttpService _httpService;

    public Processor() { }

    public Processor(IHttpService httpService, IAppSettings appSettings)
    {
        _httpService = httpService;
    }

    public async Task<HttpResponseMessage> PostToVendor(Order order)
    {
        // Building content
        var json = JsonConvert.SerializeObject(order, Formatting.Indented);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // HTTP POST
        var result = await _httpService.PostAsync("https://test.com/api", content); // returns null during the test without stepping into the method PostAsyn itself

        return result;
    }
}

测试课程:

public class MyTests
{
    private readonly Mock<IHttpService> _fakeHttpMessageHandler;
    private readonly IProcessor _processor; // contains business logic
    private readonly Fixture _fixture = new Fixture();

    public FunctionTest()
    {
        _fakeHttpMessageHandler = new Mock<IHttpService>();
        _processor = new Processor(_fakeHttpMessageHandler.Object);
    }

    [Fact]
    public async Task Post_To_Vendor_Should_Return_Valid_Response()
    {
        var fakeHttpResponseMessage = new Mock<HttpResponseMessage>(MockBehavior.Loose, new object[] { HttpStatusCode.OK });

        var responseModel = new ResponseModel
        {
            success = true,
            uuid = Guid.NewGuid().ToString()
        };

        fakeHttpResponseMessage.Object.Content = new StringContent(JsonConvert.SerializeObject(responseModel), Encoding.UTF8, "application/json");

        var fakeContent = _fixture.Build<DTO>().Create(); // DTO is the body which gonna be sent to the API
        var content = new StringContent(JsonConvert.SerializeObject(fakeContent), Encoding.UTF8, "application/json");

        _fakeHttpMessageHandler.Setup(x => x.PostAsync(It.IsAny<string>(), content))
            .Returns(Task.FromResult(fakeHttpResponseMessage.Object));

        var res = _processor.PostToVendor(fakeContent).Result;
        Assert.NotNull(res.Content);
        var actual = JsonConvert.SerializeObject(responseModel);
        var expected = await res.Content.ReadAsStringAsync();
        Assert.Equal(expected, actual);
    }
}

1 个答案:

答案 0 :(得分:3)

您的问题出在模拟设置中

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class Post {
  final List<dynamic> data;

  Post({this.data});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      data: json['response'],
    );
  }
}

class LoaderPublicity extends StatefulWidget {


  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _LoaderPublicity();
  }
}

class _LoaderPublicity extends State<LoaderPublicity> {
  List allPublicity;

  @override
  void initState() {
    super.initState();
    getPublicity().then((value) {
      print(allPublicity);
    });
    print(allPublicity);
  }

  //Obtener todas las publicidades desde el api
  Future<void> getPublicity() async {
    var response = await http.post(
        'http://contablenift.co:3008/consult/getGeneralPublicity',
        body: {'actividad': "Turistico", 'location': "No"});
    print('????????2');
    if (response.statusCode == 200) {
      setState(() {
        allPublicity = Post.fromJson(json.decode(response.body)).data;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    getPublicity();
    // TODO: implement build
    return Container(
      child: Column(
        children: <Widget>[Text('Aqui va la publicidad')],
      ),
    );
  }
}

PostAsync方法的第二个参数应该为_fakeHttpMessageHandler.Setup(x => x.PostAsync(It.IsAny<string>(), content)) .Returns(Task.FromResult(fakeHttpResponseMessage.Object)); ,但是由于StringContent是引用类型,因此您在模拟中设置的content与您在处理器中创建的content不同。如果将其更改为下一个,它将按预期工作:

content

P.S。对PostAsync的响应为null表示该方法具有默认设置,这意味着它将返回默认值