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
相关产品推荐
相关产品推荐

