我希望这不是太模糊,但我只是在学习使用NUnit和Moq进行假货的单元测试。我理解这些概念,并且我可以毫不费力地为执行简单任务的逻辑方法编写测试,例如操纵某些值或调用伪造服务。但我有点试图弄清楚如何测试如下所示的东西,这是一种需要进行数据调用并具有多个依赖关系的方法。
这是我必须根据数据库对用户进行身份验证的类:
class Authenticator: IAuthenticator
{
private IConnectionHelper _connHelper;
public Authenticator(IConnectionHelper connHelper)
{
_connHelper = connHelper;
}
public string UserId { get; set; }
public string Password { get; set; }
public UserModel User { get; private set; }
public bool ValidateUser()
{
if (string.IsNullOrEmpty(UserId))
return false;
if (string.IsNullOrEmpty(Password))
return false;
using (Entities db = new Entities(_connHelper))
{
MD5 md5 = MD5.Create();
byte[] inputBytes = Encoding.ASCII.GetBytes(this.Password);
byte[] hash = md5.ComputeHash(inputBytes);
string md5Hash = BitConverter.ToString(hash).ToUpper().Replace("-", string.Empty);
var query = from u in db.Users
where u.UserId.ToUpper().Trim() == this.UserId.ToUpper()
where u.CPassword.Trim() == md5Hash
select u;
this.User = query.FirstOrDefault();
}
if (this.User == null)
{
Log.LogLine(TraceLevel.Verbose, string.Format("Authentication failed for user '{0}'", this.UserId));
return false;
}
else
{
Log.LogLine(TraceLevel.Verbose, string.Format("Successfully authenticated user '{0}'", this.UserId));
return true;
}
}
}
我想为此创建一个测试夹具,但我不确定如何处理它。
我可以模拟IConnectionHelper,并测试如果UserId或Password为null / empty,该方法将失败,但这是我能得到的。如果IConnectionHelper是假的,那么我显然无法测试数据库的东西,我不确定我应该做什么。有人可以在这里阐明最佳做法吗?
修改
StuartLc接受的答案肯定让我朝着正确的方向前进。我确实需要为Entity Framework做一些额外的设置工作,基本上使用它作为参考:http://msdn.microsoft.com/en-us/data/dn314429.aspx
答案 0 :(得分:5)
根据Ryan的评论,您的代码与依赖项Entities
和Log
紧密耦合,以便于单元测试(无需诉诸Moles
或Fakes
)。
作为一般规则,使用new
创建任何实质性依赖关系,或使用static
方法(如Log
)通常是阻止使用{{1}等框架进行隔离单元测试的罪魁祸首}}
我建议的是重构代码如下:
Moq
类(可能是像EF Entities
或Linq DbSet
这样的ORM工件)创建到工厂中的问题分开。DataContext
依赖项移至工厂中。IConnectionHelper
,它的生命周期也由你的IoC容器管理。 Authenticator
和Logger
都可以注入你应该结束以下的事情:
EntitiesFactory
,您现在可以 public interface IEntitiesFactory
{
Entities Create();
}
public interface ILog
{
void LogLine(TraceLevel level, string message);
}
class Authenticator : IAuthenticator
{
private readonly IEntitiesFactory _entitiesFactory;
private readonly ILog _log;
public Authenticator(IEntitiesFactory entitiesFactory, ILog log)
{
_entitiesFactory = entitiesFactory;
_log = log;
}
public string UserId { get; set; }
public string Password { get; set; }
public UserModel User { get; private set; }
public bool ValidateUser()
{
if (string.IsNullOrEmpty(UserId))
return false;
if (string.IsNullOrEmpty(Password))
return false;
using (var db = _entitiesFactory.Create())
{
MD5 md5 = MD5.Create();
byte[] inputBytes = Encoding.ASCII.GetBytes(this.Password);
byte[] hash = md5.ComputeHash(inputBytes);
string md5Hash = BitConverter.ToString(hash).ToUpper().Replace("-", string.Empty);
var query = from u in db.Users
where u.UserId.ToUpper().Trim() == this.UserId.ToUpper()
where u.CPassword.Trim() == md5Hash
select u;
this.User = query.FirstOrDefault();
}
if (this.User == null)
{
_log.LogLine(TraceLevel.Verbose, string.Format("Authentication failed for user '{0}'", this.UserId));
return false;
}
else
{
_log.LogLine(TraceLevel.Verbose, string.Format("Successfully authenticated user '{0}'", this.UserId));
return true;
}
}
出现实体工厂和记录器,现在为找到/未找到的方案提供虚假Mock
数据,并验证是否已将正确的内容发送给{{ 1}}等,即
Users