You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpecFlow无货商品访问场景步骤定义与异常断言实现问题

问题根因

你当前的代码有3个核心问题导致无法触发预期异常:

  • 步骤类中Given方法里硬编码new的Product实例仅存在于步骤类的字段中,没有注入到_productService依赖的_productRepo仓储实现里,服务层调用GetProductById时读取的是仓储返回的真实/默认数据,和你构造的测试商品完全无关。
  • 步骤正则和feature文本不匹配:Then步骤的正则要求文本结尾带on the screen,但feature里的Then步骤没有这段内容,SpecFlow根本找不到匹配的步骤绑定。
  • Feature里用了<notavailable>、<exception>占位符但没有定义Scenario Outline和对应的Examples表,占位符不会被替换为实际测试值,参数绑定也无法正常工作。

核心修复逻辑

要让测试商品和服务调用关联,核心是通过Mock框架把你构造的测试商品设置为仓储GetProductById方法的返回值,这样服务层调用仓储时就能拿到你预设的、库存为0的测试数据,进而触发售罄异常。


优化后的SpecFlow场景设计

放弃硬编码单一场景,用Scenario Outline覆盖所有库存分支的测试用例,场景描述用业务可读的表述,避免技术化措辞:

@product
Scenario Outline: Access product with different stock status
    Given there is 1 product with <Stock> inventory in the store
    When I open the product detail page for this product
    Then the page should show <ExpectedResult>

    Examples:
        | Stock | ExpectedResult                                  |
        | 0     | Sold out error                                  |
        | 3     | Product info with low stock tag                 |
        | 10    | Normal product info                             |

优化后的步骤实现

以下实现基于Moq做仓储Mock,如果你用其他Mock框架逻辑完全一致:

[Binding]
public class ProductAccessSteps
{
    private readonly ScenarioContext _scenarioContext;
    private readonly Mock<IProductRepository> _mockProductRepo;
    private readonly IProductService _productService;
    private Product _testProduct;
    private Exception _thrownException;
    private Product _returnedProduct;

    public ProductAccessSteps(ScenarioContext scenarioContext)
    {
        _scenarioContext = scenarioContext;
        // 初始化Mock仓储和服务实例,如果用SpecFlow DI可直接注入
        _mockProductRepo = new Mock<IProductRepository>();
        _productService = new ProductService(_mockProductRepo.Object);
    }

    [Given(@"there is 1 product with (.*) inventory in the store")]
    public void GivenThereIsTestProductWithSpecifiedStock(int stock)
    {
        // 构造对应库存的测试商品
        _testProduct = new Product
        {
            Id = 1,
            ProductCode = "123code",
            Name = "Eye Lashes",
            Variant = null,
            Price = 10.00m,
            Availability = stock,
            Description = "Good Product",
            SkuCode = "123sh12",
            SecondaryCategoryId = 1
        };
        // 关键配置:让仓储查询ID=1的商品时返回我们构造的测试对象
        _mockProductRepo.Setup(repo => repo.GetProductById(1)).Returns(_testProduct);
    }

    [When(@"I open the product detail page for this product")]
    public void WhenIOpenProductDetailPage()
    {
        try
        {
            _returnedProduct = _productService.GetProductById(1);
        }
        catch (Exception ex)
        {
            _thrownException = ex;
        }
    }

    [Then(@"the page should show (.*)")]
    public void ThenPageShouldShowExpectedResult(string expectedResult)
    {
        switch (expectedResult)
        {
            case "Sold out error":
                Assert.NotNull(_thrownException);
                Assert.IsType<IndexOutOfRangeException>(_thrownException);
                Assert.Contains("Product is not available. Sold Out", _thrownException.Message);
                break;
            case "Product info with low stock tag":
                Assert.Null(_thrownException);
                Assert.NotNull(_returnedProduct);
                // 校验低库存逻辑是否生效
                Assert.Equal(-1, _returnedProduct.Availability);
                break;
            case "Normal product info":
                Assert.Null(_thrownException);
                Assert.NotNull(_returnedProduct);
                Assert.Equal(_testProduct.Availability, _returnedProduct.Availability);
                break;
        }
    }
}

提示:如果要测试ID非法的分支,单独加一个用例即可,在When步骤传入id=0,断言抛出NullReferenceException即可。


内容的提问来源于stack exchange,提问作者chivas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:57:18