我正在尝试为依赖于依赖项的方法编写单元测试,该依赖项提供了接受对象并对其进行修改的方法,但没有在“新路径”上返回它,例如作为返回值或按引用参数。
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
对象实例?
答案 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);
答案 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获取产品,还是要直接传递产品。