Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何对一个使用EntityFramework6、Linq的MVC项目进行单元测试,该项目也相当依赖于数据库?_C#_Unit Testing_Asp.net Mvc 5_Entity Framework 6 - Fatal编程技术网

C# 如何对一个使用EntityFramework6、Linq的MVC项目进行单元测试,该项目也相当依赖于数据库?

C# 如何对一个使用EntityFramework6、Linq的MVC项目进行单元测试,该项目也相当依赖于数据库?,c#,unit-testing,asp.net-mvc-5,entity-framework-6,C#,Unit Testing,Asp.net Mvc 5,Entity Framework 6,我知道这是一个非常广泛的问题,这实际上是我的主要问题。我是单元测试新手,我不知道在哪里可以找到有用的资源,因为我甚至不知道到底要搜索什么。我被指派将单元测试添加到MVC网站项目中,这是我的团队第一次尝试实现这两种方法,但是我们使用的旧方法已经过时,并且没有进行优化,因此我们正在尝试采用更新和更高效的方法 新的MVC项目仍然很小,它是一个旧的遗留网站的重写,该网站非常庞大,无法管理,手动测试在每一个小改动后都需要数小时才能完成。我们希望构建更好的东西,并且相信MVC、实体框架和单元测试是实现这一点

我知道这是一个非常广泛的问题,这实际上是我的主要问题。我是单元测试新手,我不知道在哪里可以找到有用的资源,因为我甚至不知道到底要搜索什么。我被指派将单元测试添加到MVC网站项目中,这是我的团队第一次尝试实现这两种方法,但是我们使用的旧方法已经过时,并且没有进行优化,因此我们正在尝试采用更新和更高效的方法

新的MVC项目仍然很小,它是一个旧的遗留网站的重写,该网站非常庞大,无法管理,手动测试在每一个小改动后都需要数小时才能完成。我们希望构建更好的东西,并且相信MVC、实体框架和单元测试是实现这一点的方法。下面是我如何根据我们研究过的其他MVC项目设置我们的项目

这是一个基本的控制器操作结果

public ActionResult SignUp()
{
    return View("SignUp");
}

[HttpPost]
public ActionResult SignUp(UserSignUpView userSignUpView)
{
    if (User.Identity.IsAuthenticated)
    {
        return RedirectToAction("Index", "Home");
    }
    else if (!ModelState.IsValid)
    {
        return View(userSignUpView);
    }

    string error = userManagerModel.CheckIfAccountExists(userSignUpView.LoginName, userSignUpView.Email);

    if (error != null)
    {
        ModelState.AddModelError("", error);
        return View("SignUp");
    }
    else
    {
        userManagerModel.AddAccount(userSignUpView);
        FormsAuthentication.SetAuthCookie(userSignUpView.LoginName, false);
        return RedirectToAction("Welcome", "Home");
    }
}
这是一个基本模型

public class UserSignUpView
{
    [Key]
    public int UserID { get; set; }

    [Display(Name = "Login ID")]
    [Required(ErrorMessage = "* Required")]
    public string LoginName { get; set; }

    [Display(Name = "Email")]
    [Required(ErrorMessage = "* Required")]
    [EmailAddress(ErrorMessage = "Invalid Email Address")]
    public string Email { get; set; }

    [Display(Name = "Password")]
    [Required(ErrorMessage = "* Required")]
    [RegularExpression(@"^(?=.*[!@#$&*])(?=.*[0-9])(?=.*[a-zA-Z]).{8,20}$",
        ErrorMessage = "Password must be between 8 and 20 characters long. Password must also contain at least 1 upper case letter, 1 number, and 1 special character.")]
    public string Password { get; set; }

    [Display(Name = "Confirm Password")]
    [Required(ErrorMessage = "* Required")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}
下面是SignUp()操作的关联帮助程序方法

    public string CheckIfAccountExists(string loginName, string email)
    {
        using (var db = new Database())
        {
            if (db.Users.Where(o => o.LoginName.Equals(loginName)).Any()
                && db.Users.Where(o => o.Email.Equals(email)).Any())
            {
                return "Both Login and Email already exists";
            }
            else if (db.Users.Where(o => o.LoginName.Equals(loginName)).Any())
            {
                return "Login Name already taken.";
            }
            else if (db.Users.Where(o => o.Email.Equals(email)).Any())
            {
                return "An account with this email already exists.";
            }
        }
        return null;
    }

    public void AddAccount(UserSignUpView userSignUpView)
    {
        using (var db = new Database())
        {
            string securePassword = //method that adds salt and hashes userSignUpView.Password
            User user = new User();
            user.LoginName = userSignUpView.LoginName;
            user.SecurePassword = securePassword;
            user.Email = userSignUpView.Email;

            db.Users.Add(user);
            db.SaveChanges();
        }
    }
因此,我试图添加单元测试,从我所读到的内容来看,模拟数据库是我最好的选择,但我所有的尝试都失败了。到处都有关于linq、实体框架、模拟数据、mvc和单元测试的资源,但我还没有找到适合2008年以后编写的所有这些工具的资源。有没有更好的方法来设置这些部分,使其与单元测试更紧密?我可能听上去像个孩子在抓救命稻草,但我们只是想摆脱长期以来我们一直坚持的可怕的传统做法


TLDR;关于MVC实体框架的单元测试,有没有合适的最新教程?

给出简单的控制器示例,如何使该控制器更易于测试

作为启动程序,您应该考虑提取执行依赖关系。 因此,这意味着对

userManagerModel
表单身份验证进行抽象。(尽量避免耦合到静态类)

公共接口IUserManager{ 字符串检查是否存在帐户(字符串登录名、字符串电子邮件); void AddAccount(UserSignUpView UserSignUpView); } 公共接口IAAuthenticationService{ void SetAuthCookie(字符串用户名,bool createPersistentCookie); }
这样,当您想要测试控制器的操作时,就可以用伪/模拟的实现来替换它

公共类AccountController:控制器{
IUserManager用户管理器模型;
i用于身份验证的身份验证服务;
公共帐户控制器(IUserManager用户管理器模型,IAAuthenticationService表单身份验证){
this.userManagerModel=userManagerModel;
this.formsAuthentication=formsAuthentication;
}
公共行动结果注册(){
返回视图();
}
[HttpPost]
公共操作结果注册(UserSignUpView UserSignUpView){
if(User.Identity.IsAuthenticated){
返回重定向到操作(“索引”、“主页”);
}如果(!ModelState.IsValid),则为else{
返回视图(userSignUpView);
}
string error=userManagerModel.CheckIfAccountExists(userSignUpView.LoginName,userSignUpView.Email);
if(错误!=null){
AddModelError(“,error);
返回视图(userSignUpView);
}否则{
userManagerModel.AddAccount(userSignUpView);
formsAuthentication.SetAuthCookie(userSignUpView.LoginName,false);
返回操作(“欢迎”、“回家”);
}
}
}
看起来很像您最初使用的,但是您会注意到,
userManagerModel
是一个接口,而不是一个具体的类

然后,您可以手动或使用您选择的模拟框架创建假货/模拟,以测试控制器的功能,而不必将控制器与数据库紧密耦合

您将确保生产类实现接口并提供生产中预期的功能

比如说

公共类FormsAuthenticationService:IAAuthenticationService{
public void SetAuthCookie(字符串用户名,bool createPersistentCookie){
SetAuthCookie(用户名,createPersistentCookie);
}
}

您是否愿意接受关于重构代码以使其更易于测试的建议?当然可以。我想我必须重构这么多的数据库调用和对数据库的总体依赖。单元测试似乎不能很好地使用这种方法,这正是我们一直使用的方法。我想找到最好的方法,而不仅仅是我们习惯的方法。可能的答案太多了,或者好的答案对于这种格式来说太长了。请添加详细信息以缩小答案集或隔离可以在几段中回答的问题。@Nkosi这主要是我的问题实际上,我很难找到相关资源,因为我的搜索参数太宽,无法找到有用的资料。不过,我会尝试用一种更具体的方式重写它。无论如何,谢谢你抽出时间。这已经比我读过的任何东西都有用了,所以谢谢你。我知道依赖关系并不理想,我只是不确定最好的解决方法。看看这篇文章@Nkosi this is perfect,似乎在试图查找某个方面的信息时,从未出现过这样的博客和文章。另外,对于接口和静态类,我现在至少有一些更具体的东西需要研究。“再次谢谢你。”小巴曼很乐意帮忙。是的,静态类是单元测试的死敌:)但它们很容易被抽象打败。