我正在尝试测试域服务,该服务将在下订单后发送电子邮件。该服务具有私有方法,因此我在公共接口上调用了一个称为私有服务方法的方法。问题是我似乎无法检查电子邮件上的抄送,因为这是私有方法。 我知道唯一的办法来解决这个问题,如果该值被保存为接口属性等,但事实并非如此。参见下面的代码。
public int SendConsolidatedDespatchNotifications(int countOfWorkDays)
{
var sent = 0;
var trackings = _despatchLineRepository.GetTrackingWithoutDespatchNotificationInPreviousWorkDays(countOfWorkDays);
var trackingsWithinOrder = trackings == null
? new List<OrderLineTracking>()
: trackings.Where(dl => dl.DespatchReference != null).ToList();
trackingsWithinOrder.GroupBy(ot => ot.OrderKey).ForEach(
ot =>
{
if (SendConsolidatedDespatchNotifications(ot))
{
_despatchLineRepository.SetAsSent(ot.Select(ol => ol.DespatchLine));
sent++;
}
});
return sent;
}
private bool SendConsolidatedDespatchNotifications(IGrouping<int, OrderLineTracking> orderTrackingLines)
{
if (orderTrackingLines == null)
return false;
if (orderTrackingLines.Key == 0)
return false;
if (orderTrackingLines.Any())
{
var firstLine = orderTrackingLines.First();
var allOrderLines = _orderLineRepository.GetOrderLinesByOrderKey(firstLine.OrderKey);
var partiallyDespatchedLines = FindPartiallyDespatchedLines(orderTrackingLines);
var notDespatchedLines = FindNotDespatchedLines(allOrderLines, orderTrackingLines);
return SendConsolidatedDespatchedEmail(firstLine.DespatchReference, orderTrackingLines, partiallyDespatchedLines, notDespatchedLines);
}
return false;
}
private bool SendConsolidatedDespatchedEmail(
string poNumber,
IEnumerable<OrderLineTracking> despatchedLines,
IEnumerable<OrderLineTracking> partiallyDespatchedLines,
IEnumerable<OrderLine> notDespatchLines)
{
//we just assume that one PO have always just one order
var firstDespatchedLine = despatchedLines.First();
var order = firstDespatchedLine.OrderLine.OrderHeader;
if (order?.Customer == null)
return false;
var despatchGroups = new List<DespatchLineGroup>();
despatchedLines.GroupBy(dl => dl.DespatchReference).ForEach(
dl => despatchGroups.Add(
new DespatchLineGroup
{
DespatchReference = dl.Key,
DespatchedLines = dl,
TrackingWebLink = GetTrackingWebLinkFor(dl.First())
}));
var despatchNotificationEmail = new DespatchConsolidatedNotificationEmail(
order.Customer,
order,
despatchGroups,
CreateNotDespatchedItemsList(partiallyDespatchedLines, notDespatchLines));
var ccCustomer = _customerRepository.GetByCostCentreIdentifier(order.CostCentreIdentifier, order.Customer.Key);
var ccOnBasket = ccCustomer?.CostCentre;
if (ccOnBasket == null)
{
despatchNotificationEmail.To.Add(new EmailAddress(order.Customer.FullName, order.Customer.Login));
}
else
{
FillInSubaccountDetails(despatchNotificationEmail, ccCustomer, order, order.Customer, ccOnBasket);
}
despatchNotificationEmail.PopulateContentWithTags();
despatchNotificationEmail.SendAfter = firstDespatchedLine.DespatchDate;
despatchNotificationEmail.Save();
_log.InfoFormat("Despatch email {0} for {2} sent to {1}", "DespatchConsolidatedNotificationEmail", order.Customer.Login, poNumber);
return true;
}
private void FillInSubaccountDetails(
EmailTemplate email,
Customer ccCustomer,
OrderHeader order,
Customer masterAccount,
CostCentre ccOnBasket)
{
//send notifications to CostCentre account, which is on basket
email.To.Add(new EmailAddress(ccCustomer.FullName, ccCustomer.Login));
if (ccOnBasket.ReceiveNotifications) //send notifications to master only if CC is set so
{
email.To.Add(new EmailAddress(masterAccount.FullName, masterAccount.Login));
}
if (order.OrderPlacedBy.HasValue) //PD-2140 Sending email to Purchaser as well
{
var purchaser = _customerRepository.Get(order.OrderPlacedBy.Value);
if (purchaser?.Purchaser != null && purchaser.Purchaser.ReceiveNotifications)
{
email.To.Add(new EmailAddress(purchaser.FullName, purchaser.Login));
}
}
if ( order.ApprovedBy != null)
{
var approver = _customerRepository.Get(order.ApprovedBy.Value);
if(approver?.Approver != null && //has approver and its not MAH
approver.Approver.ReceiveNotifications)
email.To.Add(new EmailAddress(approver.FullName, approver.Login));
}
}
//this inherits from EmailTemplate which has save method.
public class DespatchConsolidatedNotificationEmail : EmailTemplate
{
public DespatchConsolidatedNotificationEmail() { }
public DespatchConsolidatedNotificationEmail(
Customer customer,
OrderHeader orderHeader,
List<DespatchLineGroup> despatchLines,
List<NotDespatchedLine> notDespatchLines)
{
AddEmailData(customer);
AddEmailData(orderHeader);
AddEmailData(despatchLines);
AddEmailData(notDespatchLines);
}
}
//below is the save method
public int Save()
{
var manageSave = Configuration.Container.Resolve<IWantToManageSaving>();
return manageSave.Save(this);
}
注意Email实现了一个抽象类,它是EmailTemplate而不是接口。
我想弄清楚添加了哪个emailAddress吗?
答案 0 :(得分:0)
有赞成和反对单元测试专用方法的参数。我将由您决定是否是个好主意。话虽如此,您可以使用PrivateObject
类。遵循以下原则:
Class c = new Class();
PrivateObject o = new PrivateObject(c);
var whatever = o.Invoke("FillInSubaccountDetails");
Assert.AreEqual(whatever, expected);
这里有一个问题,因为您的方法返回void,没有要声明的返回值。您可能需要调整方法?
答案 1 :(得分:0)
因此,根据您提供的所有代码:
您的Save
方法闻起来很糟糕。您不应该使用IoC容器来手动解决依赖关系。如果您通过ctor注入了IWantToManageSaving
(我喜欢这样命名:)),则可以在测试中对其进行模拟。如果您有var savingManagerMock = new Mock<IWantToManageSaving>()
,则可以在单元测试中验证Save
方法是用正确设置的EmailTemplate
实例调用的。像这样:
// ASSERT
savingManagerMock.Verify(x => x.Save(It.IsAny<EmailTemplate>(
arg => arg.To.Contains(/* ... */));
类似的东西,取决于您想要的实际断言。
另一种方法是将DespatchConsolidatedNotificationEmail
的结构抽象到工厂IDespatchConsolidatedNotificationEmailFactory
中,使其返回DespatchConsolidatedNotificationEmail
的特殊模型,并设置其Save
方法,例如,保存EmailTemplate
的当前状态,然后声明它。但是,我仍然倾向于第一个解决方案。
最后,请注意:测试此方法相当复杂。这通常意味着它可以写得更好。在这种情况下,我看到两个危险信号,第一个是明确使用Container
,这可以通过依赖项注入(手动解析而不是不注入:P)来避免。其次是这种方法相当复杂!它调用了许多私有方法,并且有很多逻辑无法通过快速阅读该方法来理解。您应该考虑将这些私有方法拆分为另一个类中的internal
帮助方法,这些方法将分别进行单元测试。然后,您可以在测试此公共方法时信任它们,因为它们在那时基本上是一个依赖项,因此可以设置模拟程序并断言正确的internal
方法已被调用并且该方法的协定已得到满足。