繁体   English   中英

如何正确测试? (C#,NUnit,Moq)

[英]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的评论,您的代码与EntitiesLog的依赖关系太紧密,以至于难以进行单元测试(而不必求助于MolesFakes )。

通常,使用new创建任何实质性的依赖关系,或者使用诸如Logstatic方法通常是防止使用Moq框架进行隔离单元测试的罪魁祸首。

我建议将代码重构如下:

  1. 通过接口将创建Entities类(大概是EF DbSet或Linq DataContext类的ORM工件)创建到工厂中的担忧分开。
  2. IConnectionHelper依赖项可以移入工厂。
  3. 还应该将静态记录器剥离到一个接口中,并且还应该将一个实例注入到Authenticator并且其寿命也应由IoC容器管理。 LoggerEntitiesFactory都可以注入

您应该完成如下所示的操作:

  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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM