FakeItEasy 模拟对象修改方法
FakeItEasy to mock an object-modifying method
我正在尝试为依赖依赖项的方法编写单元测试,该依赖项提供接受对象并修改它的方法,但不 return 它在 "new path" 上,例如作为 return 值或按引用参数。
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
class' IsProductNew()
方法:
var fakeFixer = A.Fake<IFixer>();
var manager = new Manager(fakeFixer);
var isNew = manager.IsProductNew(A<int>._);
Assert.True(isNew);
我在这里缺少的是:如何模拟 IFixer.Modify()
的行为,即让它修改 Product
对象实例?
So I want to be able to test my Manager class' IsProductNew() method
您无法以当前形式对其进行有效测试。您不会获得 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 获取产品,还是希望直接传递产品。
有效回答这个问题取决于 GetProduct(id);
的定义
但是,如果 IFixer
实现没有影响或不良行为,那么就真的没有必要模拟它。
//Arrange
var fixer = new Fixer();
var manager = new Manager(fixer);
//Act
var isNew = manager.IsProductNew(1);
//Assert
Assert.True(isNew);
但要回答
How do I mock the behaviour of IFixer.Modify()
, i.e. have it modify a Product object instance?
您需要一个捕获匹配参数的回调
//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
我正在尝试为依赖依赖项的方法编写单元测试,该依赖项提供接受对象并修改它的方法,但不 return 它在 "new path" 上,例如作为 return 值或按引用参数。
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
class' IsProductNew()
方法:
var fakeFixer = A.Fake<IFixer>();
var manager = new Manager(fakeFixer);
var isNew = manager.IsProductNew(A<int>._);
Assert.True(isNew);
我在这里缺少的是:如何模拟 IFixer.Modify()
的行为,即让它修改 Product
对象实例?
So I want to be able to test my Manager class' IsProductNew() method
您无法以当前形式对其进行有效测试。您不会获得 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 获取产品,还是希望直接传递产品。
有效回答这个问题取决于 GetProduct(id);
但是,如果 IFixer
实现没有影响或不良行为,那么就真的没有必要模拟它。
//Arrange
var fixer = new Fixer();
var manager = new Manager(fixer);
//Act
var isNew = manager.IsProductNew(1);
//Assert
Assert.True(isNew);
但要回答
How do I mock the behaviour of
IFixer.Modify()
, i.e. have it modify a Product object instance?
您需要一个捕获匹配参数的回调
//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);