如何解决有没有办法在服务中模拟使用 Moq 服务并执行代码?
我有一个使用 Moq 的测试问题。我到处都看过,但我不太明白到底发生了什么。
我想测试一个名为 Basket 的类(服务),在该类中我作为 DI 传递另一个类(服务)discountService。
我面临的问题是,每当我模拟 Basket 类时,它都会很好地触发,但是当它到达在 discountService 中实际执行另一个方法的代码时,它只是跳过该方法而不是进入。做错了吗?
我想可能是我没有正确地进行设置?
这是我目前的实际测试代码:
[Test]
public async System.Threading.Tasks.Task Test1Async()
{
//Arrange
var basketServiceMock = new Mock<IBasket>();
var discountServiceMock = new Mock<IdiscountService>();
List<BasketProductModel> productsModel =
new List<BasketProductModel>
{
new BasketProductModel()
{
ProductName = "Milk",ProductPrice = 10,Quantity = 3
}
};
BasketModel basket = new BasketModel { Products = productsModel };
basketServiceMock.Setup(x => x.Baskett)
.Returns(new BasketModel { Products = productsModel });
var product = new BasketProductModel()
{
ProductName = "Milk",Quantity = 1
};
var product1 = new BasketProductModel()
{
ProductName = "Milk",Quantity = 1
};
var product2 = new BasketProductModel()
{
ProductName = "Milk",Quantity = 1
};
var product3 = new BasketProductModel()
{
ProductName = "Milk",Quantity = 1
};
discountServiceMock.Setup(x => x.Applydiscount(ref basket,product));
var basketService = new Basket.Basket(discountServiceMock.Object);
//Act
await basketService.AddProductToBasket(product);
await basketService.AddProductToBasket(product1);
await basketService.AddProductToBasket(product2);
await basketService.AddProductToBasket(product3);
//Assert
}
这是我的篮子类:
public class Basket : IBasket
{
private BasketModel _basketProducts;
private double _totalCost;
public BasketModel Baskett {
get {
if (_basketProducts == null)
throw new ArgumentNullException("xxxxxxxxxx");
return _basketProducts;
}
}
public double TotalCost
{
get
{
if (_totalCost == null)
{
throw new ArgumentNullException("xxxxxxxxxx");
}
return _totalCost;
}
}
private readonly IdiscountService _discountService;
public Basket(IdiscountService discountService)
{
_discountService = discountService;
_basketProducts = new BasketModel();
_basketProducts.Products = new List<BasketProductModel>();
}
public async Task AddProductToBasket(BasketProductModel product)
{
if (product == null)
throw new ArgumentNullException();
var obj = _basketProducts.Products.FirstOrDefault(x => x.ProductName == product.ProductName);
if (obj == null)
_basketProducts.Products.Add(product);
else
{
obj.Quantity += product.Quantity;
}
_discountService.Applydiscount(ref _basketProducts,product); <---This part is being skipped,do I miss something in the SETUP part?!
}
}
这是我的 discountService 类,它没有被触发(Applydiscount 方法)
public class discountService : IdiscountService
{
public static readonly ReadOnlyCollection<string> discountedProducts = new List<String> {
"Milk","Butter","Bread",}.AsReadOnly();
public void Applydiscount(ref BasketModel basket,BasketProductModel product)
{
if (discountedProducts.Contains(product.ProductName) && basket.Products.Count > 0)
product.Calculatediscount(basket);
}
}
这是 BasketProductModel(篮子里的产品)
public class BasketProductModel : discountProcess
{
public BasketProductModel()
{
Init(this);
}
public string ProductName { get; set; }
public double ProductPrice { get; set; }
public int Quantity;
public int Freebies { get; set; }
public double Total { get; set; }
}
这是上面类中使用的抽象类,用于使用一些预先配置的实现
public abstract class discountProcess
{
private BasketProductModel _obj { get; set; }
public BasketProductModel Obj
{
get
{ // check _obj is inited:
if (_obj == null) throw new Exception();
return _obj;
}
}
protected void Init(BasketProductModel bPModel)
{
_obj = bPModel;
}
public void Calculatediscount(BasketModel basket)
{
var productContained = NumberOfProductsInBasket(ref basket,_obj);
var obj = basket.Products.FirstOrDefault(x => x.ProductName == _obj.ProductName);
var freeItems = 0;
switch (_obj.ProductName)
{
case "Milk":
if (productContained % 2 != 0)
{
obj.Freebies = numberOfFreeItems(obj.Quantity);
//AddQuantity(ref obj);
}
break;
case "Butter":
break;
case "Bread":
productContained = NumberOfProductsInBasket(ref basket,productName: "Butter");
if (productContained % 2 == 0)
{
CalculatediscountedPrice(50,ref obj);
}
break;
default:
break;
}
Calculatetotal(ref obj);
}
//...Other Methods...
}
所以基本上我甚至希望能够访问服务内部的代码并执行所有代码,以便我可以断言篮子:)
知道如何实现这一点吗?
最终目标:
我希望 Baskett
拥有基于 discountService 对 ref 传递的 basked 所做的操作的数据,以便我最终可以断言。
解决方法
如果 Basket
是被测对象,则不应嘲笑它。
//...
//Arrange
var basketServiceMock = new Mock<IBasket>(); //<-- Should not be mocked
var discountServiceMock = new Mock<IDiscountService>();
//...
相反,使用实际实例,
var discountServiceMock = new Mock<IDiscountService>();
Basket basketServiceMock = new Basket(discountServiceMock .Object);
//...
并模拟完成测试用例所需的依赖项。
本例中的主题在其构造函数中初始化模型,您尝试在测试中设置该模型。由于模型是由主题类初始化的,因此无需在测试中尝试创建一个。只需根据测试需要填充它。
查看以下内容
[Test]
public async System.Threading.Tasks.Task Test1Async() {
//Arrange
var discountServiceMock = new Mock<IDiscountService>();
Basket subject = new Basket(discountServiceMock.Object);
List<BasketProductModel> productsModel = new List<BasketProductModel> {
new BasketProductModel() {
ProductName = "Milk",ProductPrice = 10,Quantity = 3
}
};
subject.Baskett.Products = productsModel;
// Only matches if the ref argument to the invocation is the same instance
discountServiceMock
.Setup(x => x.ApplyDiscount(ref subject.Baskett,It.IsAny<BasketProductModel>()));
//You need to decide what you want the mocked member to do.
var product = new BasketProductModel() {
ProductName = "Milk",Quantity = 1
};
var product1 = new BasketProductModel() {
ProductName = "Milk",Quantity = 1
};
var product2 = new BasketProductModel() {
ProductName = "Milk",Quantity = 1
};
var product3 = new BasketProductModel() {
ProductName = "Milk",Quantity = 1
};
//Act
await subject.AddProductToBasket(product);
await subject.AddProductToBasket(product1);
await subject.AddProductToBasket(product2);
await subject.AddProductToBasket(product3);
//Assert
//...You need to decide what it is you are actually testing
//and want to assert
}
观察:
被测成员被定义为 async
,但该成员实际上没有等待。重新检查该成员的设计。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。