2

背景:

根据我的新雇主严格的单元测试要求,我正在努力获得一些单元测试经验,但单元测试作为一个整体对我来说是新的。由于依赖于

HttpContext.Current.GetOwinContext().Authentication

HttpContext.Current.GetOwinContext().GetUserManager

现在,这个当前的项目到目前为止还没有完全利用接口和依赖注入,但是作为将我们组织的 Active Directory 系统合并到我们的身份数据库中的一部分(当某人使用有效的 Active 登录时,在用户表中为他们创建一行目录凭据,然后从那时起为他们使用身份数据库),我一直致力于使用接口和 Ninject 改造我们的身份方面,并将 FakeItEasy 添加到我们的测试单元测试项目中。

这确实意味着我们目前正在生成使用实际数据库本身的测试,而不是伪造的东西。我们知道这是一种不好的做法,这样做只是为了在我们处理第一个真正的项目时不让我们新人的头脑超负荷。我们已经解决了(大部分/所有)由此引起的问题,并且我们的测试在完成后会清理它们。

问题:
我在尝试对以下方法进行单元测试时遇到了一个特殊问题:

public bool ResetPassword(User user)
    {
        if (!user.EmailConfirmed) return false;

        user.RequirePasswordReset = true;
        string randomGeneratedPassword = GenerateRandomPassword(20); // defined at end of class
        user.PasswordHash = hash.HashPassword(randomGeneratedPassword);

        if (!UpdateUser(user)) return false;

        string message = "We have received a request to reset your password. <br /><br />" +
            "Your new password is shown below. <br /><br />" +
            $"Your new password is: <br />{randomGeneratedPassword}<br /><br />" +
            "This password is only valid for one login, and must be changed once it is used. <br /><br /><br /><br />" +
            "Server<br />AmTrust Developer University";

        SendFormattedEmail(user.Email, user.FullName, message, "Your password has been reset");
        return true;
    }

到目前为止,我为此编写的测试(省略了测试用例)是:

public bool ResetPasswordTests(bool emailConfirmed)
    {
        //arrange
        _user = new User()
        {
            Email = "ttestingly@test.com",
            EmailConfirmed = emailConfirmed,
            FirstName = "Test",
            isActive = true,
            isActiveDirectoryAccount = false,
            LastName = "Testingly",
            PasswordHash = _hash.HashPassword("secret1$"),
            RequirePasswordReset = false,
            UserName = "ttestingly"
        };

        string hashedPass = _user.PasswordHash;

        _identityContext.Users.Add(_user);
        _identityContext.SaveChanges();

        //Suppress the email sending bit!
        A.CallTo(() => _userBusinessLogic_Testable.SendFormattedEmail(null, null, null, null, null))
        .WithAnyArguments()
        .DoesNothing();

        //act
        bool result = _userBusinessLogic_Testable.ResetPassword(_user);

        //assert
        Assert.That(result);
        Assert.That(_user.PasswordHash != hashedPass);
        Assert.That(_user.RequirePasswordReset);
        return result;
    }

运行这个测试(对于它所有的各种测试用例)返回以下异常:

System.NotSupportedException:无法检查模型兼容性,因为数据库不包含模型元数据。只能检查使用 Code First 或 Code First 迁移创建的数据库的模型兼容性。

这是由_identityContext.Users.Add(_user);

我所看到的有关此问题的所有内容都表明它是由在运行代码试图连接到该数据库时打开的数据库连接引起的,我不认为是这种情况,或者是由于尝试让 EF 管理 pre - 现有数据库(事实并非如此:我在测试之间多次删除了我的数据库以尝试验证这一点)。

注意:目前我们团队的所有数据库都只是 localhost 数据库,所以没有其他人在弄乱我的东西。

我已经看到了一个例子,其中解决方案是更改连接字符串,但是这个问题只发生在单元测试中 - 我已经验证,在运行时,应用程序上的所有内容在我为合并接口所做的任何更改之前都按预期工作、Ninject 和 Active Directory - 所以我认为连接字符串本身不是问题,但这里是相关的连接字符串
(它们是正确的 XML,但我不知道如何让它们在 Stack Overflow 上正确显示,所以我删除了所有的大括号):

connectionStrings
add name="ADUUserDB" providerName="System.Data.SqlClient" connectionString="Data Source=localhost\sql2014;Initial Catalog=ADUUserDB;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;MultipleActiveResultSets =True"
/连接字符串

4

2 回答 2

2

我没有关于数据库内容的信息,但我认为我对A.CallTo. FakeItEasy 不高兴被要求配置Single,这是一种扩展方法。您应该只配置假货。也许

A.CallTo(() => _userBusinessLogic.GetUsers(null, null, null, null, null, null, null))
                                 .WithAnyArguments()
                                 .Returns(new [] { _user });

公平地说,FakeItEasy 可以更好地指出问题所在(尽管通常会在A.CallTo. 我创建了issue 786来跟踪这个。

于 2016-07-01T19:56:01.387 回答
2

我弄清楚了这个问题是什么,令人尴尬的是,这是一个简单的配置问题——我实际上并没有伪造我试图测试的 BLL 正在使用的存储库。我直接跳到数据库上下文,它以前可以工作,但由于某种原因,我当时使用的新的、部分伪造的设置失败了。

所以,我必须做的改变很简单:
private IUserRepository _userRepository = A.Fake<IUserRepository>();
用 A.CallTo(); 伪造相关的底层存储库调用;

这也消除了向数据库添加内容然后在测试结束时将其删除的需要,这也是引发错误的地方。

于 2016-07-06T15:57:17.870 回答