[英]How to test this properly? (C#, NUnit, Moq)
我希望这不太模糊,但是我只是在学习使用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接受的答案肯定使我朝着正确的方向前进。 我确实需要为实体框架做一些额外的设置工作,基本上将其用作参考: http : //msdn.microsoft.com/zh-cn/data/dn314429.aspx
根据Ryan的评论,您的代码与Entities
和Log
的依赖关系太紧密,以至于难以进行单元测试(而不必求助于Moles
或Fakes
)。
通常,使用new
创建任何实质性的依赖关系,或者使用诸如Log
的static
方法通常是防止使用Moq
框架进行隔离单元测试的罪魁祸首。
我建议将代码重构如下:
Entities
类(大概是EF DbSet
或Linq DataContext
类的ORM工件)创建到工厂中的担忧分开。 IConnectionHelper
依赖项可以移入工厂。 Authenticator
并且其寿命也应由IoC容器管理。 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
实体工厂和记录器,并为发现/未发现的场景提供虚假的Users
数据,并验证是否将正确的内容发送到了logger
等。
[Test]
public void SomeTest()
{
var mockFactory = new Mock<IEntitiesFactory>();
var mockEntities = new Mock<Entities>();
var fakeUsers = new List<UserModel>
{
new UserModel
{
UserId = "Bob",
CPassword = "TheHashOfSecret"
}
};
mockEntities.SetupGet(_ => _.Users).Returns(fakeUsers.AsQueryable());
mockFactory.Setup(_ => _.Create()).Returns(mockEntities.Object);
var mockLog = new Mock<ILog>();
var sut = new Authenticator(mockFactory.Object, mockLog.Object)
{
UserId = "Bob",
Password = "Secret"
};
Assert.DoesNotThrow(() => sut.ValidateUser());
Assert.IsNotNull(sut.User);
mockLog.Verify(_ => _.LogLine(), Times.Once); ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.