简体   繁体   English

如何使用本地 json 文件从模拟端点获取结果?

[英]How can I get the results from mock a endpoint with a local json file?

I'm pretty new in .NET and I have to mock GetCustomerDetailsAsync endpoint using a local json file in order to get the file results and create a way to enable and disable the mocking mechanism.我是 .NET 的新手,我必须使用本地 json 文件模拟 GetCustomerDetailsAsync 端点,以便获取文件结果并创建一种启用和禁用 mocking 机制的方法。 Here is the handler where I have to implement the logic这是我必须在其中实现逻辑的处理程序

  public class GetCustomerDetailsQueryHandler : IRequestHandler<GetCustomerDetailsQuery, CustomerInsertionModel>
{
    private readonly ICustomerApiClient _customerApiClient;
    private readonly IAccountApiClientGraphQl _accountApiClientGraphQl;
    private readonly IMapper _mapper;
    private readonly IMediator _mediator;
    private readonly IOptions<CommonFeaturesOptions> _commonFeaturesOptions;
    public GetCustomerDetailsQueryHandler(ICustomerApiClient customerApiClient,
                                          IAccountApiClientGraphQl accountApiClientGraphQl,
                                          IMediator mediator,
                                          IMapper mapper,
                                          IOptions<CommonFeaturesOptions> commonFeaturesOptions)
    {
        _customerApiClient = customerApiClient ?? throw new ArgumentNullException(nameof(customerApiClient));
        _accountApiClientGraphQl = accountApiClientGraphQl ?? throw new ArgumentNullException(nameof(accountApiClientGraphQl));
        _mediator = mediator;
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
        _commonFeaturesOptions = commonFeaturesOptions ?? throw new ArgumentNullException(nameof(commonFeaturesOptions));
    }

 public async Task<CustomerInsertionModel> Handle(GetCustomerDetailsQuery request, CancellationToken cancellationToken)
    {
        //if(!EnablePostfixations){
        //var customerApiResponse = (await _customerApiClient.GetCustomerDetailsAsync(_mapper.Map<GetCustomerDetailsRequest>(request)))
        //                                  .CustomerDetails.FirstOrDefault();
        //}
        //else
        //{
        //var response = await _customerApiClient.GetCustomerDetailsAsync(
        //                             _mapper.Map<GetCustomerDetailsRequest>((request, _commonFeaturesOptions.Value)));
       var customerApiResponse = (await _customerApiClient.GetCustomerDetailsAsync(_mapper.Map<GetCustomerDetailsRequest>(request)))
                              .CustomerDetails.FirstOrDefault();
          if (customerApiResponse is null)
        {
            return new CustomerInsertionModel((false, string.Empty))
                        .MarkCustomerAsNotFound();
        }

        var customer = new CustomerInsertionModel(customerApiResponse.Cif,
            customerApiResponse.CnpCui,
            customerApiResponse.Category,
            customerApiResponse.Insolvency,
            request.AddInsolvency,
            request.CloseCustomerCategories.ToList());

        return customer 
       } 
   }

commented lines are just for guidence (what to do, inject an IOptions with a boolean that decide when to enable and disable the mocking mechanism ) plus I wanna know where should I have to deserialize the file(in handler or outside of it?)注释行仅供参考(要做什么,注入一个带有 boolean 的 IOptions,它决定何时启用和禁用 mocking 机制)另外我想知道我应该在哪里反序列化文件(在处理程序中还是在处理程序之外?)

 public class DeserializeFromFileAsync
{
    public class PostfixationsFile
    {
        public string Customer_no { get; set; }
        public string First_name { get; set; }
        public string Last_name { get; set; }
        public string Customer_type { get; set; }
        public string Adress_line { get; set; }
        public int Cnp { get; set; }
        public string Customer_category { get; set; }
        public int Open_branch { get; set; }
        public string Branch_name { get; set; }
        public string Insolventa { get; set; }
    }

    public class Program
    {
        public static async Task Main()
        {
            string fileName = @"FullPath";
            using FileStream openStream = File.OpenRead(fileName);
            PostfixationsFile weatherForecast = await JsonSerializer.DeserializeAsync<PostfixationsFile>(openStream);
        }
    }
}

and this is the json file.这是 json 文件。

[
 { "customer_no": "03242524",
    "first_name": "Prenume",
    "last_name": "Nume",
    "customer_type": "PF",'
    "adress_line": " Str. FN  Nr.    Bl.    Sc.    Et.    Ap.    Sect.    Loc.    Jud.  ",
    "cnp": "1970907336523",
    "customer_category": "PF_ONLINE",
    "open_branch": "213",
    "branch_name": "SUCURSALA DEJ",
    "insolventa": "NU"
},
{ "customer_no": "03242524",
    "first_name": "Prenume",
    "last_name": "Nume",
    "customer_type": "PF",'
    "adress_line": " Str. FN  Nr.    Bl.    Sc.    Et.    Ap.    Sect.    Loc.    Jud.  ",
    "cnp": "1970907336523_J77",
    "customer_category": "PF_ONLINE",
    "open_branch": "213",
    "branch_name": "SUCURSALA DEJ",
    "insolventa": "NU"
 }
]

Sorry for this messy message but is first time when I ask something here.很抱歉收到这条乱七八糟的消息,但这是我第一次在这里提问。

I wasn't able to build your code as it has a lot of dependencies which I'd need to make all sorts of assumptions about - you may want to read about creating a minimal, reproducible example .我无法构建您的代码,因为它有很多依赖项,我需要对这些依赖项做出各种假设 - 您可能需要阅读有关创建最小的、可重现的示例的信息。

However, I should be able to help you to understand how to use mocks.但是,我应该能够帮助您了解如何使用模拟。 The first thing to know about mocks is that they should only be used in unit tests, not in your production code.关于 mock 的第一件事是它们应该只用于单元测试,而不是在你的生产代码中。 So you wouldn't want to pass a flag into your actual handler to tell it whether or not to run in "mock" mode.所以您不想将标志传递给您的实际处理程序来告诉它是否以“模拟”模式运行。 Instead, you can create an instance of a mock in your unit test and then inject the mock into the code you're testing, so that your unit test can concentrate on the behaviour of the code you're testing, rather than the behaviour of any external code / web service / database etc that it is dependent on.相反,您可以在单元测试中创建一个模拟实例,然后将模拟注入到您正在测试的代码中,这样您的单元测试就可以专注于您正在测试的代码的行为,而不是它所依赖的任何外部代码/web 服务/数据库等。

Imagine this is the class you want to test (equivalent of your GetCustomerDetailsQueryHandler ).假设这是您要测试的 class(相当于您的GetCustomerDetailsQueryHandler )。

    public class MyHandler
    {
        private readonly IThingToMock thingToMock;

        public MyHandler(IThingToMock thingToMock)
        {
            this.thingToMock = thingToMock;
        }

        public string MethodToTest(int id)
        {
            var request = new RequestModel { Id = id };
            var response = this.thingToMock.GetInformation(request);
            return response.Foo;
        }

        public async Task<string> MethodToTestAsync(int id)
        {
            var request = new RequestModel { Id = id };
            var response = await this.thingToMock.GetInformationAsync(request).ConfigureAwait(false);
            return response.Foo;
        }
    }

This is dependent on IThingToMock (equivalent of your ICustomerApiClient ), which looks like this:这取决于IThingToMock (相当于您的ICustomerApiClient ),它看起来像这样:

    public interface IThingToMock
    {
        ResponseModel GetInformation(RequestModel request);
        Task<ResponseModel> GetInformationAsync(RequestModel request);
    }

And IThingToMock 's request and response models look like this (I've deliberately kept them really simple): IThingToMock的请求和响应模型如下所示(我故意让它们非常简单):

    public class RequestModel
    {
        public int Id { get; set; }
    }

    public class ResponseModel
    {
        public string Foo { get; set; }
    }

Before you can start mocking, you need to add a unit test project to your solution (in Visual Studio's new project wizard, choose the template for a xUnit, nUnit or MSTest unit test project - which one you use is a matter of personal preference, I'm using xUnit for this answer).在开始之前 mocking,您需要为您的解决方案添加一个单元测试项目(在 Visual Studio 的新建项目向导中,选择 xUnit、nUnit 或 MSTest 单元测试项目的模板——您使用哪个是个人喜好问题,我正在使用 xUnit 来回答这个问题)。

Next, add a mocking NuGet package to your test project and add the appropriate using directive to your unit tests, eg using Moq;接下来,将 mocking NuGet package 添加到您的测试项目,并将适当的using指令添加到您的单元测试中,例如using Moq; . . I'm using Moq here, but again, other mocking packages are available, and it's up to you which one you want to use.我在这里使用的是最小起订量,但同样,其他 mocking 包也可用,您要使用哪一个取决于您。

Now you can write a unit test, like this:现在你可以写一个单元测试,像这样:

        [Fact]
        public void MethodToTest_AnyRequest_ReturnsExpectedResponse()
        {
            // Arrange
            var mockThing = new Mock<IThingToMock>();
            var expectedFoo = "Bar";
            mockThing.Setup(m => m.GetInformation(It.IsAny<RequestModel>()))
                .Returns(new ResponseModel { Foo = expectedFoo });
            var handler = new MyHandler(mockThing.Object);

            // Act
            var actualFoo = handler.MethodToTest(1);

            // Assert
            Assert.Equal(expectedFoo, actualFoo);
        }

Let's break this down.让我们分解一下。 First, we create an instance of Mock<IThingToMock> .首先,我们创建一个Mock<IThingToMock>的实例。 This has an Object property, which is an implementation of IThingToMock with all its methods and properties, except that by default the methods do nothing and any members with a return value return the default value for their return type (usually null ).它有一个Object属性,它是IThingToMock及其所有方法和属性的实现,除了默认情况下这些方法不执行任何操作并且任何具有返回值的成员返回其返回类型的默认值(通常为null )。

Next, we use the Setup method to make our mock behave in the way we want.接下来,我们使用 Setup 方法使我们的模拟以我们想要的方式运行。 In this example, we're saying that its GetInformation method should always return a particular ResponseModel regardless of the properties of the RequestModel which were passed to it.在这个例子中,我们说它的GetInformation方法应该总是返回一个特定的ResponseModel而不管传递给它的RequestModel的属性。 I'm telling it to return a hard-coded value here, but you could tell the mock to return the data deserialized from your JSON file instead.我在这里告诉它返回一个硬编码值,但您可以告诉模拟返回从 JSON 文件反序列化的数据。

And then at the end of the Arrange step, we're injecting the mocked IThingToMock into the constructor of the MyHandler class, which is the class we want to test the behaviour of.然后在 Arrange 步骤结束时,我们将模拟的IThingToMock MyHandler构造函数,这是我们要测试其行为的 class。

To control the behaviour of an asynchronous method, set up the mock using the ReturnsAsync method instead of Returns , for example:要控制异步方法的行为,请使用ReturnsAsync方法而不是Returns设置模拟,例如:

        [Fact]
        public async Task MethodToTestAsync_AnyRequest_ReturnsExpectedResponse()
        {
            // Arrange
            var mockThing = new Mock<IThingToMock>();
            var expectedFoo = "Bar";
            mockThing.Setup(m => m.GetInformationAsync(It.IsAny<RequestModel>()))
                .ReturnsAsync(new ResponseModel { Foo = expectedFoo });
            var handler = new MyHandler(mockThing.Object);

            // Act
            var actualFoo = await handler.MethodToTestAsync(1);

            // Assert
            Assert.Equal(expectedFoo, actualFoo);
        }

Hopefully these examples give you sufficient insight into how mocks are used in unit tests to be able to apply the principles to your own situation.希望这些示例能让您充分了解如何在单元测试中使用模拟,以便能够将这些原则应用于您自己的情况。 You may also want to read How YOU can Learn Mock testing in .NET Core and C# with Moq , which provides more examples of how to use Moq, this time with nUnit rather than xUnit, as well as some good practice around unit testing generally.您可能还想阅读.NET Core 和 C# 中的 How YOU can Learn Mock testing with Moq ,其中提供了更多有关如何使用 Moq 的示例,这次是使用 nUnit 而不是 xUnit,以及一些关于单元测试的一般良好实践。 And of course, be sure to consult the documentation for whichever mocking package you're using.当然,请务必查阅您使用的任何 mocking package 的文档。

Happy mocking!快乐的嘲笑!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM