【问题标题】:FakeItEasy to mock an object-modifying methodFakeItEasy 模拟对象修改方法
【发布时间】:2019-11-15 12:37:30
【问题描述】:

我正在尝试为依赖于依赖项的方法编写单元测试,该依赖项提供了一种接受对象并修改它的方法,但不会在“新路径”上返回它,例如作为返回值或引用参数。

public class Product
{
    public string Name { get; set; }
}

public interface IFixer
{
    void Modify(Product product);
}

public class Fixer: IFixer
{
    public void Modify(Product product)
    {
        if (string.IsNullOrEmpty(product.Name))
        {
            product.Name = "Default";
        }
    }
}

public class Manager()
{
    private readonly IFixer _fixer;

    public Manager(IFixer fixer)
    {
        _fixer = fixer;
    }

    public bool IsProductNew(int id)
    {
         var product = GetProduct(id); // Gets an object instance from a repository, e.g. a file or a database, so we can have something to operate on. 

         _fixer.Modify(product);

         return product.Name != "Default";
    }
}

所以我希望能够测试我的Manager 类'IsProductNew() 方法:

var fakeFixer = A.Fake<IFixer>();

var manager = new Manager(fakeFixer);

var isNew = manager.IsProductNew(A<int>._);

Assert.True(isNew);

我在这里缺少的是:如何模拟 IFixer.Modify() 的行为,即让它修改 Product 对象实例?

【问题讨论】:

  • 什么是GetProduct(id);做?它是在哪里定义的?
  • 它只是一个占位符,表示将有多种Product对象实例可用,因此具有修改功能是有实际价值的。

标签: c# unit-testing fakeiteasy


【解决方案1】:

有效回答这个问题取决于GetProduct(id);的定义

如果IFixer 实现没有连锁反应或不良行为,那么真的没有必要模拟它。

//Arrange
var fixer = new Fixer();    
var manager = new Manager(fixer);

//Act
var isNew = manager.IsProductNew(1);

//Assert
Assert.True(isNew);

但要回答

如何模拟 IFixer.Modify() 的行为,即让它修改 Product 对象实例?

你需要一个回调来捕获匹配的参数

//Arrange
var fakeFixer = A.Fake<IFixer>();
A.CallTo(() => fakeFixer.Modify(A<Product>._))
    .Invokes((Product arg) => arg.Name = "Not Default Name");

var manager = new Manager(fakeFixer);

//Act
var isNew = manager.IsProductNew(1);

//Assert
Assert.True(isNew);

参考Invoking Custom Code

【讨论】:

    【解决方案2】:

    所以我希望能够测试我的 Manager 类的 IsProductNew() 方法

    您无法以当前形式对其进行有效测试。您不会获得 100% 的覆盖率。您应该任一

    直接传递产品:

    public bool IsProductNew(Product product)
    {
         _fixer.Modify(product);
    
         return product.Name != "Default";
    }
    

    您可以通过以下方式进行测试:

    var fakeFixer = A.Fake<IFixer>();
    var manager = new Manager(fakeFixer);
    var product = new Product { Id = 100, Name = "Default" }; // create another test for where Name != "Default"
    var isNew = manager.IsProductNew(product);
    
    // Assert that fixer.modify is called with the product
    A.CallTo(() => fakeFixer.Modify(A<product>.That.Matches(p => p.Id == 100))).ShouldHaveBeenCalledOnceExactly();
    
    // Should return false because of product name = "Default"
    Assert.IsFalse(isNew);
    

    ,将“product getter”传递给 Manager() 构造函数:

    public Manager(IFixer fixer, IProductGetter productGetter)
    {
        _fixer = fixer;
        _productGetter = productGetter;
    }
    ...
    
    public bool IsProductNew(int id)
    {
         var product = _productGetter.GetProduct(id);
    
         _fixer.Modify(product);
    
         return product.Name != "Default";
    }
    

    您可以通过以下方式进行测试:

    var fakeProductGetter = A.Fake<IProductGetter>();
    
    // Prime the product getter to return a product
    A.CallTo(() => fakeProductGetter.Get(A<int>.Ignored))
        .Returns(new Product{
            Id = 100,
            Name = "Default" // create another test for where Name != "Default"
        });
    
    var fakeFixer = A.Fake<IFixer>();
    
    var manager = new Manager(fakeFixer, fakeproductGetter);
    
    var isNew = manager.IsProductNew(100);
    
    // Assert that a call is made to productGetter.Get with the ID
    A.CallTo(() => productGetter.Get(100)).MustHaveHapennedOnceExactly();
    
    // Assert that fixer.modify is called with the product
    A.CallTo(() => fakeFixer.Modify(A<product>.That.Matches(p => p.Id == 100))).ShouldHaveBeenCalledOnceExactly();
    
    // Should return false because of product name = "Default"
    Assert.IsFalse(isNew);
    

    您所做的选择取决于您是否希望 IsProductNew() 方法负责通过 ID 获取产品,或者您是否只想直接传递产品。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多