FakeIt轻松模拟对象修改方法

时间:2019-11-15 12:37:30

标签: c# unit-testing fakeiteasy

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

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对象实例?

2 个答案:

答案 0 :(得分:2)

有效地解决此问题取决于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

答案 1 :(得分:0)

  

所以我希望能够测试我的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);

,将“产品吸气剂”传递到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获取产品,还是要直接传递产品。