我有一个想要测试的复杂方法,它接收用户、UserUpdatedDetails(密码、恢复问题和电子邮件)和密码。
它应该检查以下内容:
- 确保用户更新了他必须更新的所有字段
- 独立验证每个字段
- 确保密码与用户密码匹配(验证)
- 更新必填字段
- 如果密码/问题答案已更改,请重新加密敏感数据的特殊密钥(它是一个随机长密钥,用于加密敏感数据,例如生日,名字和东西......密钥也通过密码加密保存和问题的答案不是现在的重点)。
主要方法使用小方法,即使用其他小方法。问题是它们都是私有方法,没有理由不应该这样做(我不会在其他任何地方使用它们)
测试这一切将是一场噩梦,即使我将独立测试每个小方法,它也不能让我清楚地了解主要方法正在做它必须做的事情。
实现看起来像这样:
UpdateUserDetails(this User user,
UserDetails userDetails,
string Password,
out ErrorList<AuthErrors> errorList)
{
UpdateActions actions;
errorList = user.ValidateUserDetails(userDetails, out actions);
if (errorList.IsSuccess() && actions != UpdateActions.None)
{
var status = Auth(user.UserId, password); // Easy to mock
if (status.IsSuccess)
{
try
{
var newUser = user.UpdateUserDetails(userDetails, actions);
commit;
return newUser;
}
catch
{
rollback;
throw;
}
}
else
errorList.Add(AuthErrors.WrongPassword);
}
return null;
}
enum UpdateActions
{
None = 0,
Password = 1,
Email = 2,
Question = 4,
All = Password & Email & Question
}
编辑:顺便说一句,我很容易模拟 Auth 实现,以及 DAL 实现(更新用户)所有其他都是问题......
只是为了对方法内部的方法给出一些看法:
ValidateUserDetails(this User user,
UserDetails userDetails,
out UpdateActions actions)
{
actions = UpdateActions.None;
ErrorList<AuthErrors> errorList = new ErrorList<AuthErrors>()
if (userDetails.password != null || user.RequirePasswordUpdate)
{
errorList.AddRange(validatePassword(userDetails.password) // PublicMethod, already, which has tests.
actions.Add(UpdateActions.Password) // ExtensionMethod
}
if (userDetails.email != null || user.RequireEmailUpdate)
{
errorList.AddRange(validateEmail(userDetails.Email) // PublicMethod, already, which has tests.
actions.Add(UpdateActions.Email) // ExtensionMethod
}
if (userDetails.Question != null || userDetails.QuestionAnswer || user.RequireQuestionUpdate)
{
errorList.AddRange(validateQuestion(userDetails.Question, userDetails.QuestionAnswer) // PublicMethod, already, which has tests.
actions.Add(UpdateActions.Question) // ExtensionMethod
}
}
UpdateUserDetails(this User user, UserDetails userDetails, UpdateActions actions, string oldPassword)
{
if (actions.Has(UpdateActions.UpdatePassword)
user.UpdatePassword(userDetails.password)
...
return DataAccess.UpdateUser(user); // Easy to Mock
}
UpdatePassword(this User user, string password, string oldPassword)
{
user.Password = _IEncryptionManager.BcryptEncrypt(password); // easy to mock encryption methods
user.SensKey = _IEncryptionManager.DesEncrypt(_IEncryptionManager.DesDecrypt(user.SensKey,
oldPassword),
password);
}
感谢您的帮助,
谢谢问候,
阿米尔。