在我的工作中,我们正在编写由应用程序调用的Web服务。我们正在使用域驱动设计以敏捷的思维方式进行工作。与DDD中一样,我们具有域和应用程序层。但是,在为这些层编写单元测试时,我们遇到了问题,因为似乎我们在域单元测试和应用程序单元测试中两次测试了域逻辑:
应用单元测试
[TestMethod]
public void UserApplicationService_SignOut_ForExistingUserWithBalance_ShouldClearBalanceAndSignOut()
{
//Arrange
long merchantId = 1;
long userId = 1;
var transactionId = "001";
var id = "122";
var user = Help.SetId<User>(User.Register(id, new DateTime(2015, 01, 01, 00, 00, 01)), userId);
_usersDb.Add(user);
var userBonusBalanceRepository = _testContext.MoqUnitOfWork.MockUnitOfWork.Object.GetUserBonusAccountRepository();
UserBonusAccount uba = userBonusBalanceRepository.GetForUser(user);
uba.PayTo(
new Domain.Core.Money { TotalAmount = 10, BonusAmount = 0 },
new Domain.Core.Outlet
{
BonusPercentage = 50,
IsLoyalty = true,
Id = id,
OutletId = "111"
},
transactionId,
DateTime.Now);
userBonusBalanceRepository.Update(uba);
//Act
_testContext.UserApplicationService.SignOut(id);
//Assert
var firstOrDefault = this._balances.FirstOrDefault(x => x.UserId == user.Id && x.MerchantId == merchantId);
Assert.IsTrue(firstOrDefault != null && firstOrDefault.Balance == 0);
Assert.IsNotNull(this._transactions.Where(x => x.Id == transactionId && x.Type == BonusTransactionType.EraseFunds));
}
域单元测试
[TestMethod]
public void UserBonusAccount_ClearBalances_shouldClearBalancesForAllMerchants()
{
long userId = 1;
long firstMerchantId = 1;
long secondMerchantId = 2;
User user = User.Register("111", new DateTime(2015, 01, 01, 00, 00, 01));
Shared.Help.SetId(user, userId);
List<BonusTransaction> transactions = new List<BonusTransaction>();
List<BonusBalance> balances = new List<BonusBalance>();
var userBonusAccount = UserBonusAccount.Load(transactions.AsQueryable(), balances.AsQueryable(), user);
userBonusAccount.PayTo(new Money {TotalAmount = 100, BonusAmount = 0},
new Outlet
{
BonusPercentage = 10,
IsLoyalty = true,
MerchantId = firstMerchantId,
OutletId = "4512345678"
}, "001", DateTime.Now);
userBonusAccount.PayTo(new Money {TotalAmount = 200, BonusAmount = 0},
new Outlet
{
BonusPercentage = 10,
IsLoyalty = true,
MerchantId = secondMerchantId,
OutletId = "4512345679"
}, "002", DateTime.Now);
userBonusAccount.ClearBalances();
Assert.IsTrue(userBonusAccount.GetBalanceAt(firstMerchantId) == 0);
Assert.IsTrue(userBonusAccount.GetBalanceAt(secondMerchantId) == 0);
}
如您所见,这两个测试都检查用户余额是否为0,这是域责任。因此,问题是:应用程序层单元测试应该看起来如何,应该测试什么?我读过某个地方,单元测试应该在“用于流控制的应用程序服务和用于业务规则的域模型”中进行测试。有人可以详细说明并举例说明应测试什么样的应用程序层单元测试吗?
应用程序服务的职责包括输入验证,安全性和事务控制。所以这是您应该测试的!
以下是应用服务单元测试应提供的一些示例问题,并回答:
我的应用服务吗?
根据您实现这些方面的精确程度,测试它们可能有意义,也可能没有意义。例如,安全性通常是以声明性的方式实现的(例如,具有C#属性)。在这种情况下,您可能会发现一种代码检查方法比使用单元测试检查每个应用程序服务的安全属性更合适。但是YMMV。
另外,请确保您的单元测试是实际的单元测试,即对所有内容(特别是域对象)进行存根或模拟。在测试中尚不清楚是这种情况(请参阅下面的旁注)。
对应用程序服务进行单元测试是一件好事。但是,在应用程序服务级别上,从长远来看,我发现集成测试更有价值。因此,我通常建议以下组合策略来测试应用程序服务:
您的单元测试包含一些代码气味。
例如,我总是直接在单元测试中实例化SUT(被测系统)。这样,您便确切知道它具有哪些依赖项,并且对其中的哪些项进行了存根,模拟或使用了真正的依赖项。在您的测试中,这一点还不清楚。
另外,您似乎依赖于字段来收集测试输出(this._balances
例如)。如果测试类仅包含一个测试,通常这不是问题,但否则可能会出现问题。通过依赖字段,您可以依赖测试方法“外部”的状态。这会使测试方法难以理解,因为您不能只阅读测试方法,而是需要考虑整个类。与过度使用设置和拆卸方法时发生的问题相同。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句