繁体   English   中英

如何测试Asp.net会员资格?

[英]How to Unit Test Asp.net Membership?

我是单元测试的新手,我正在尝试测试我写的一些.NET会员资料。

所以我试图检查我的VerifyUser方法,检查用户凭据是否有效。

所以这就是它的样子:

public bool VerifyUser(string userName, string password)
    {
        bool valid = Membership.ValidateUser(userName, password);
        return valid;
    }

现在,每次我运行单元测试时都会失败。 我知道我正在传递正确的凭据和内容。 然后我突然意识到,也许我的测试项目(与我的真实项目在同一个解决方案下)可能需要自己的web.config文件,其中包含连接字符串和内容。 或者应用程序配置文件,因为它是一个应用程序库项目。

那么我只是从我的真实项目中复制web.config文件并将其称为一天? 或者我应该只从中取出零件? 或者我离开了。

我的数据库正在使用自定义数据库,其.net成员资格与我的数据库合并。 所以在我的配置文件中,我必须指定一个ManagerProvider和一个roleProvider。

这就是我的单元测试的样子

   [Test]
   public void TestVerifyUser()
   {
      AuthenticateUser authenitcate = new AuthenticateUser();
      bool vaild = authenitcate.VerifyUser("chobo3", "1234567");


      Assert.That(vaild, Is.True);
   }

后来我在我的一个asp.net mvc ActionResult方法(准确的登录视图)我有这个:

FormsAuthentication.RedirectFromLoginPage(loginValidation.UserName,rememberMe);

那么现在我该如何编写一个能够完成用户操作的单元测试。 假设他们从主页开始,然后单击登录页面并成功登录。 我希望他们重定向到主页。

我不知道如何在代码中表示。 我很确定RedirectFromLoginPage可以工作,现在我正在测试它。 我正在测试我在登录ActionResult方法中可能发生的3件事情。

  1. 用户登录并将其发送回来。
  2. 用户无法登录并被发送回LoginView并看到错误消息。
  3. 用户已尝试转到安全状态,并已重定向到登录页面。 如果登录成功,将通过ReturnUrl重定向回安全页面。

所以我想做一个测试,看看它们是否正常工作。 这就是为什么我需要让用户来自主页,看看他们是否会被重定向到以后,如果他们来自一个安全的页面,他们会重新定向到以后。

我也是使用NUnit 2.5和VS2008 Pro的方式。


这就是我想要测试的内容。 我正处于我试图查看用户是否有效的部分(if语句)。 我不知道如何测试它。

public ActionResult Login(string returnUrl, FormCollection form, bool rememberMe)
       {
            LoginValidation loginValidation = new LoginValidation();
            try
            {
                UpdateModel(loginValidation, form.ToValueProvider());

            }
            catch
            {

                return View("Login");
            }

            if (ModelState.IsValid == true)
            {

                bool valid = authenticate.VerifyUser(loginValidation.UserName, loginValidation.Password);

                if (valid == false)
                {
                    ModelState.AddModelError("frm_Login", "Either the Password or UserName is invalid");

                }
                else if (string.IsNullOrEmpty(returnUrl) == false)
                {
                    /* if the user has been sent away from a page that requires them to login and they do 
                     * login then redirect them back to this area*/
                    return Redirect(returnUrl);
                }
                else
                {

                    FormsAuthentication.RedirectFromLoginPage(loginValidation.UserName, rememberMe);
                }

            }


            return View("Login");
        }

您可以通过将自定义成员身份代码重构为两层来测试您的控制器和大部分自定义提供程序:仅与数据库交互的数据访问存储库,以及使用存储库组件提供Membership API的服务层。 您可以在服务层验证参数,保存和强制执行EnablePasswordReset等参数,并将任何数据库异常或状态代码转换为适合控制器使用的形式。

当您使用自己的接口指定每个图层时,无论其实现方式如何,使用者都可以写入该接口。 当您的应用程序运行时,您的提供程序当然是通过这些接口与数据库通信,但是为了测试,您可以模拟存储库或服务接口。 您可以通过模拟存储库级别来测试服务层,而不必弄乱数据库或web.config文件,并且可以通过模拟服务层来测试控制器。 如果您不想重构整个提供程序,如果只创建服务接口并让控制器使用它,您仍然可以测试控制器。

具体来说,如果有点冗长,您的存储库和服务接口可能类似于:

namespace Domain.Abstract {
    public interface IRepository {
        string ConnectionString { get; }
    }
}

namespace Domain.Abstract {
    public interface IUserRepository : IRepository {
        MembershipUser CreateUser(Guid userId, string userName, string password, PasswordFormat passwordFormat, string passwordSalt,
                string email, string passwordQuestion, string passwordAnswer, bool isApproved,
                DateTime currentTimeUtc, bool uniqueEmail);
        MembershipUser GetUser(Guid userId, bool updateLastActivity, DateTime currentTimeUtc);
        PasswordData GetPasswordData(Guid userId, bool updateLastLoginActivity, DateTime currentTimeUtc);
        void UpdatePasswordStatus(Guid userId, bool isAuthenticated, int maxInvalidPasswordAttempts, int passwordAttemptWindow, 
                      DateTime currentTimeUtc, bool updateLastLoginActivity, DateTime lastLoginDate, DateTime lastActivityDate);
        //....
    }
}

namespace Domain.Abstract {
  public interface IUserService {
    bool EnablePasswordRetrieval { get; }
    bool EnablePasswordReset { get; }
    bool RequiresQuestionAndAnswer { get; }
    bool RequiresUniqueEmail { get; }
    //....

    MembershipUser CreateUser(string applicationName, string userName, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved);
    MembershipUser GetUser(Guid userId, bool userIsOnline);
    bool ValidateUser(Guid userId, string password);
    //...
    }
}

namespace Domain.Concrete {
  public class UserService : IUserService {
    private IUserRepository _userRepository;


    public UserService(IUserRepository userRepository) {
        _userRepository = userRepository;
    }
    //...
    public bool ValidateUser(Guid userId, string password) {
        // validate applicationName and password here
        bool ret = false;
        try {
            PasswordData passwordData;
            ret = CheckPassword(userId, true, true, DateTime.UtcNow, out passwordData);
        }
        catch (ObjectLockedException e) {
            throw new RulesException("userName", Resource.User_AccountLockOut);
        }
        return ret;
    }

    private bool CheckPassword(Guid userId, string password, bool updateLastLoginActivityDate, bool failIfNotApproved,
                                DateTime currentTimeUtc, out PasswordData passwordData) {
        passwordData = _userRepository.GetPasswordData(userId, updateLastLoginActivityDate, currentTimeUtc);

        if (!passwordData.IsApproved && failIfNotApproved)
            return false;

        string encodedPassword = EncodePassword(password, passwordData.PasswordFormat, passwordData.PasswordSalt);
        bool isAuthenticated = passwordData.Password.Equals(encodedPassword);

        if (isAuthenticated && passwordData.FailedPasswordAttemptCount == 0 && passwordData.FailedPasswordAnswerAttemptCount == 0)
            return true;
        _userRepository.UpdatePasswordStatus(userId, isAuthenticated, _maxInvalidPasswordAttempts, _passwordAttemptWindow,
                                            currentTimeUtc, updateLastLoginActivityDate,
                                            isAuthenticated ? currentTimeUtc : passwordData.LastLoginDate,
                                            isAuthenticated ? currentTimeUtc : passwordData.LastActivityDate);

        return isAuthenticated;
    }
}

Asp.Net Membership系统旨在在Asp.Net请求的上下文中工作。 所以,你有三个选择。

  1. 大多数人在面对这样的依赖时会在它周围写一个薄的包装器。 包装器不做任何事情,只是将所有调用重定向到底层依赖项。 所以,他们只是不测试它。 您的AuthenticateUser就是这样一个包装器。 您可能应该将所有方法设置为虚拟或提取接口以使其可模拟,但这是另一个故事。
  2. 使用TypeMock Isolator和模拟成员资格。
  3. 使用Ivonna框架并在Asp.Net上下文中运行您的测试(这将是一个集成测试)。

不幸的是,你不能只是复制你的web.config或你的app.config并让它以这种方式工作。 原因是您的程序集在NUnit进程内运行,而不是在您的应用程序下运行。

为了纠正您的情况,您可能需要模拟或存根您正在呼叫的成员资格成员,或遵循约会优先配置方法来处理您在web.config中存储的设置。

有很多模拟框架,但这里有一对: Rhino MocksMoq

另外,要遵循约定优于配置方法,您可以执行以下操作:

static ConfigurationSettings
{
     static String SomeSetting
     {
        get
        {
           var result = "HARDCODEDVALUE";
           if (ConfigurationManager.AppSettings["SOMEKEY"] != null)
               result = ConfigurationManager.AppSettings["SOMEKEY"];
           return result;
     }
}

然后你可以使用这样的代码:

//this is how the old code might look
var mySetting = ConfigurationManager.AppSettings["SOMEKEY"];
//use the setting

//this is how the new code would look
var mySetting = ConfigurationSettings.SomeSetting;
//use the setting   

这样您的测试就可以工作,当您在应用程序下运行它时,它将使用您存储的任何配置设置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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