单元测试存储库的CRUD操作 - 基于HttpContext路由到测试或实时数据库的功能使测试变得困难

时间:2016-05-19 13:36:53

标签: c# .net unit-testing asp.net-mvc-5 httpcontext

目前,我们正在开发一个利用类库的新项目。我正在为该站点开发一个功能,处理存储库的基础知识,该存储库现在具有与数据库通信的简单CRUD操作方法。我们最近决定开始尝试对代码进行单元测试,以进一步鼓励良好的编程习惯。

现在我的单元测试失败了,因为它试图在我们的Live DB上使用存储过程 - Live上当前不存在。起初它只是失败了(引发异常)并且我欺骗了HttpContext并将其传递给服务器名称URL,该URL应该触发连接到测试数据库。然而它正在连接到当前的活动我认为这导致的代码是:

public static bool TestMode()
    {
        if (
            HttpContext.Current.Request.ServerVariables["SERVER_NAME"] == "localhost" ||
            HttpContext.Current.Request.ServerVariables["SERVER_NAME"] == "test.org"
            )
            return true;
        else
            return false;
    }

该库旨在与MVC站点结合使用,因此这种类型的设置不是不必要的,不幸的是,重构我们如何做到这一点并不是我的选择。

这就是我的单元测试

[TestClass]
public class CalendarTests {

    public CalendarRepository _repo { get; set; }

    [TestInitialize]
    public void Setup() {
        _repo = new CalendarRepository();

        HttpContext.Current = new HttpContext(
                                new HttpRequest(null, "http://test.org", null),
                                new HttpResponse(null)
                              );
    }

    [TestMethod]
    public void CreateEventTest() {

        int val = _repo.CreateEvent(1,
                                    "test title",
                                    "demo-description",
                                    DateTime.Now,
                                    DateTime.Now.AddDays(1),
                                    true,
                                    1,
                                    1);

        Assert.IsTrue(val > 0);
    }
}

我所做的大部分搜索都是因为他们正在测试MVC项目中的控制器而遇到这个问题,但这有点不同,我不确定从哪里开始。我知道我需要找到一种模拟上下文的方法来测试。各种测试框架都像NUnit一样被提出,还有像NSubstitute这样的模拟框架。我对这些产品感兴趣但不确定它们的使用是否合理。

感谢任何和所有帮助!

编辑:

我尝试过的事情:

虽然我删除了代码,但我尝试编写与this类似的代码以及this

EDIT2:

这是连接字符串来自

的位置
public static string DSHA()
    {
        var db = new Database()
        {
            LiveConnectionString = "sanitized-live",
            TestConnectionString = "sanitized-test"
        };

        return ResolveConnection(db);
    }

这是代码TestMode由

调用
private static string ResolveConnection(Database db)
    {
        if (Helpers.TestMode())
            return db.TestConnectionString;
        else
            return db.LiveConnectionString;
    }

所以我要求连接字符串,它调用DSHA,导致ResolveConnection的评估,其条件是TestMode最终决定返回的连接字符串。

EDIT2:

Nkosi的回复非常有用,但是现在我正在尝试确保Helpers获得服务器真正提供的默认ServerVariables。我试图使用构造函数

public Helpers(){
  ServerVariable = new ServerVariables();
}

然而,当我在单元测试中检查Context时,我得到一个空引用异常,我认为它是因为Helpers有静态方法,但是Helpers类本身并不是静态的。

[TestMethod]
public void CheckContext(){
    var context = Helpers.ServerVariable.Name;
    Assert.IsTrue(context == "localhost");
}

我不确定使用容器注入接口的正确方法,但是希望尽可能避免更改Helpers类的行为,因为使用它编写了相当多的代码。需要提供任何其他东西可能意味着相当多的狩猎,以确保它的行为随处可见。

1 个答案:

答案 0 :(得分:1)

根据您指出的评论,您基本上需要从HttpContext获取服务器名称。

摘要包含HttpContext的服务背后的功能,只是为您提供所需的功能。

类似......

public interface IServerVariable {
    string Name { get; }
}

你可以有一个实现......

public class ServerVariables : IServerVariable {
    public string Name {
        get {
            return HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
        }
    }
}

可以根据需要模拟IServerVariable进行测试。因此,您可以直接注入接口以获取所需的数据,而不是直接引用HttpContext

您可能必须重构当前代码以允许通过容器注入接口,否则您将不得不手动执行此操作。但除此之外,这应该会使您的代码更易于测试。

...假设

public class Helpers {

    public static IServerVariable ServerVariable {get;set;}

    public static bool TestMode()
    {
        if (ServerVariable != null 
            && (ServerVariable.Name == "localhost" || ServerVariable.Name == "test.org")
            )
            return true;
        else
            return false;
    }

}

测试看起来像......

[TestClass]
public class CalendarTests {

    private class FakeServerVariable : IServerVariable {
        public string Name { get { return "test.org"; }
    }

    public CalendarRepository _repo { get; set; }

    [TestInitialize]
    public void Setup() {
        _repo = new CalendarRepository();

        Helpers.ServerVariable = new FakeServerVariable();
    }

    [TestMethod]
    public void CreateEventTest() {

        int val = _repo.CreateEvent(1,
                                    "test title",
                                    "demo-description",
                                    DateTime.Now,
                                    DateTime.Now.AddDays(1),
                                    true,
                                    1,
                                    1);

        Assert.IsTrue(val > 0);
    }
}