0

我正在尝试找出分解单元测试的正确方法。

给定以下两个类,一个是 a CategoryService,另一个是CategoryValidatorusing FluentValidation,您将如何编写这些测试?

我尝试为服务编写一个测试,为验证器编写一个测试,但是如何测试验证在服务中是否有效?或者这是否超出了服务测试的范围,应该包含在验证器测试中?

在该AddCategory方法中,我正在测试验证器中不存在类别名称。我如何在单元测试中测试它?或者那是一个集成测试?

分类服务

public class CategoryService : ValidatingServiceBase, ICategoryService
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IRepository<Category> categoryRepository;
    private readonly IRepository<SubCategory> subCategoryRepository;
    private readonly IValidationService validationService;

    public CategoryService(
        IUnitOfWork unitOfWork,
        IRepository<Category> categoryRepository,
        IRepository<SubCategory> subCategoryRepository,
        IValidationService validationService)
        : base(validationService)
    {
        this.unitOfWork = unitOfWork;
        this.categoryRepository = categoryRepository;
        this.subCategoryRepository = subCategoryRepository;
        this.validationService = validationService;
    }

    public bool AddCategory(Category category)
    {
        var validationResult = validationService.Validate(category);

        if (!validationResult.IsValid)
            return false;

        categoryRepository.Add(category);

        return true;
    }
}

类别验证器

public class CategoryValidator : AbstractValidator<Category>
{
    public CategoryValidator(ICategoryService service)
    {
        RuleFor(x => x.Name)
            .NotEmpty()
            .Must((category, name) =>
            {
                return service.GetCategories().SingleOrDefault(x => x.Name == name) == null;
            });
    }
}
4

3 回答 3

3

有些人可能会觉得这是异端,但我更喜欢对事物进行测试,即使测试接近于集成而不是单元。你只需要确保你的检查是全面的。

不要担心测试如何在类之间进行交互,如果您是从单元测试开始(我怀疑您是),那么最好编写测试,并且 TDD 的最终 D 会自然而然地到来。

于 2013-09-02T06:30:44.243 回答
1

我认为您应该分别测试验证器和服务。说对于服务测试,您可以模拟验证器以返回特定结果,而不会用太多细节(伪代码)污染您的测试逻辑

test Should_Add_Category_Only_If_It_Is_Valid() {    
  //given
  Category category = GivenACategory();
  GivenValidatorAcceptsCategory(category);

  //when
  Bool result = service.AddCategory(category);

  //then
  AssertTrue(result);
  VerifyServiceHasCategory(category);
}

test Should_Reject_Invalid_Category() {    
  //given
  Category category = GivenACategory();
  GivenValidatorRejectsCategory(category);

  //when
  Bool result = service.AddCategory(category);

  //then
  AssertFalse(result);
  VerifyServiceDoesNotHaveCategory(category);
}
于 2013-09-02T06:20:23.587 回答
1

您要解决的问题超出了单元测试的定义。单元测试测试单元;)所以为带有存根验证器的服务编写单元测试,并为带有存根服务的验证器编写单元测试。然后编写一些集成测试,这些测试将验证您的组件是否正确交互。

但是,这始终是您如何在系统中定义component(a unit) 的问题。如果您将服务和验证器的功能作为一个包一起公开,那么一起为它们编写单元测试,将它们视为一个单元,并且不要费心思考它们在内部是如何工作的——只期望特定“API”的正确结果(意思是-暴露的接口)调用。

您可以将这两种方法结合起来,将服务和验证器作为一个单元进行测试,但要注意使用白盒测试方法的内部结构。但是,对于大型组件,这是不鼓励的,我认为原因很明显。

最后——所有这些名字都重要吗?他们只是名字,我不能只有测试吗?恕我直言,重要的是要表示单元测试之间的边界 - 它应该绝对测试所有内容(意思是“每个单元” - 见上文)。这为您提供了随时重构和重构代码的灵活性和能力,而不会有更改单元外部行为的风险。第二组是附加测试(如集成、冒烟、白盒、回归测试等)——因为它们测试系统的较大部分,它们的数量要少得多。它们很重要,因为单元测试不能验证组件之间的所有交互,但它们只测试一些可能的场景。

于 2013-09-02T08:12:20.037 回答