简体   繁体   English

单元测试采用参数的 WebAPI 控制器

[英]Unit testing WebAPI Controller that takes parameters

I have a WebAPI controller that takes a parameters object like this:我有一个 WebAPI 控制器,它采用这样的参数对象:

public class InvoicesParameters : QueryStringParameters
{
    public string InvoiceType { get; set; } = "P";
    public bool Approved { get; set; } = false;
    public string Region { get; set; } = null;
    public string VendorCode { get; set; } = null;
}

The controller works just fine.控制器工作正常。 I can pass any or all of the params and get the expected results.我可以传递任何或所有参数并获得预期结果。 Now I am trying to add a unit test for this controller using xUnit.现在我正在尝试使用 xUnit 为这个控制器添加一个单元测试。 I've tried several different methods all with various failing results.我尝试了几种不同的方法,但结果各不相同。

My test looks like this:我的测试是这样的:

[Theory(DisplayName = "Get Invoices")]
[ClassData(typeof(InvoicesParametersTestData))]
public void GetInvoices_ShouldReturnInvoices(InvoicesParameters iparams)
{
    var mockService = new Mock<IShopAPService>();
    mockService.Setup(x => x.GetInvoices(iparams))
        .Returns(GetSampleInvoices());

    var controller = new InvoicesController(mockService.Object);

    IHttpActionResult actionResult = controller.GetInvoices(iparams);
    var contentResult = actionResult as OkNegotiatedContentResult<List<Invoice>>;

    Assert.NotNull(contentResult);
    Assert.NotNull(contentResult.Content);
    Assert.Equal(3, contentResult.Content.Count);
}

private List<InvoiceDto> GetSampleInvoices()
{
    List<InvoiceDto> output = new List<InvoiceDto>
    {
        new InvoiceDto
        {
            Id = 1,
            VendorCode = "PFSATL",
            VendorName = "PlantStone LLC",
            Type = "I",
            PONumber = "54P-00007365",
            OrderId  = "940817",
            InvoiceType = "P",
            InvoiceNumber = "DT1010816950",
            InvoiceDate = Convert.ToDateTime("01/01/2020"),
            InvoiceAmount = 2496.48M,
            Region = "WEST",
            ShopNumber = "16",
            ApprovalDate = null,
            Approver = null
        },
        new InvoiceDto
        {
            Id = 2,
            VendorCode = "NATSLC",
            VendorName = "National American Doodads",
            Type = "I",
            PONumber = "72P-00004838",
            OrderId  = "874566",
            InvoiceType = "P",
            InvoiceNumber = "123454321789",
            InvoiceDate = Convert.ToDateTime("01/01/2020"),
            InvoiceAmount = 457.88M,
            Region = "WEST",
            ShopNumber = "16",
            ApprovalDate = null,
            Approver = null
        },
        new InvoiceDto
        {
            Id = 3,
            VendorCode = "BRIDG",
            VendorName = "Bridgeport Soda",
            Type = "I",
            PONumber = "54P-0074865",
            OrderId  = "741258",
            InvoiceType = "P",
            InvoiceNumber = "987456",
            InvoiceDate = Convert.ToDateTime("01/01/2020"),
            InvoiceAmount = 74.99M,
            Region = "WEST",
            ShopNumber = "16",
            ApprovalDate = null,
            Approver = null
        }
    };

    return output;
}


public class InvoicesParametersTestData
{
    public static IEnumerable<InvoicesParameters> TestData
    {
        get
        {
            yield return new InvoicesParameters { InvoiceType = "P", Approved = false };
            yield return new InvoicesParameters { InvoiceType = "P", Approved = true };
        }
    }
}

Something is not right with this pattern.这种模式有些不对劲。 I am getting a compile time error of ClassData must point to a valid class.我收到 ClassData 的编译时错误必须指向一个有效的类。 Is there a better way to do this?有一个更好的方法吗? or a way to fix what I have?或修复我所拥有的东西的方法?

Your InvoicesParametersTestData class should look like this to make it work:您的InvoicesParametersTestData类应如下所示以使其工作:

public class InvoicesParametersTestData : IEnumerable<object[]>
{
    private readonly List<object[]> _testData = new List<object[]>
    {
        new object[]
        {
            new InvoicesParameters
            {
                InvoiceType = "P", Approved = false
            },
            new InvoicesParameters
            {
                InvoiceType = "P",
                Approved = true
            }
        }
    };

    public IEnumerator<object[]> GetEnumerator() => _testData.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

To be able to use a class with the DataClass attribute, the class has to inherit from IEnumerable<object[]> .为了能够使用具有 DataClass 属性的类,该类必须从IEnumerable<object[]>继承。 The specified type has to be an object , otherwise xUnit will throw an error.指定的类型必须是object ,否则 xUnit 将抛出错误。

Because you are implementing IEnumerable , you also have to implement the GetEnumerator() methods.因为您正在实施IEnumerable ,所以您还必须实施GetEnumerator()方法。

EDIT编辑

The error The test method expected 1 parameter value, but 2 parameter values were provided.错误The test method expected 1 parameter value, but 2 parameter values were provided. is caused by _testData in class InvoicesParametersTestData which returns 2 items.是由类InvoicesParametersTestData_testData引起的,它返回 2 个项目。 To fix that, your test method should accept two parameters.为了解决这个问题,您的测试方法应该接受两个参数。 Like: GetInvoices_ShouldReturnInvoices(InvoicesParameters iparams, InvoicesParameters iparams2) .像: GetInvoices_ShouldReturnInvoices(InvoicesParameters iparams, InvoicesParameters iparams2)

But , this would make for an odd test.但是,这将是一个奇怪的测试。

A better way of setting this up, is by using the InlineData attribute:更好的设置方法是使用InlineData属性:

[Theory(DisplayName = "Get Invoices")]
[InlineData(true)]
[InlineData(false)]
public void GetInvoices_ShouldReturnInvoices(bool approved)
{
    var iparams = new InvoicesParameters
    {
        InvoiceType = "P",
        Approved = approved
    };

    var mockService = new Mock<IShopAPService>();
    mockService.Setup(x => x.GetInvoices(iparams))
        .Returns(GetSampleInvoices());

    var controller = new InvoicesController(mockService.Object);

    IHttpActionResult actionResult = controller.GetInvoices(iparams);
    var contentResult = actionResult as OkNegotiatedContentResult<List<Invoice>>;

    Assert.NotNull(contentResult);
    Assert.NotNull(contentResult.Content);
    Assert.Equal(3, contentResult.Content.Count);
}

You won't need the InvoicesParametersTestData class anymore.您将不再需要InvoicesParametersTestData类。

If you would want to make InvoiceType variable as well, you could do it like this:如果您还想让 InvoiceType 变量,您可以这样做:

[InlineData(true, "P")]
[InlineData(false, "X")]
public void GetInvoices_ShouldReturnInvoices(bool approved, string invoiceType)

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

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