对控制器进行单元测试 - 如何处理连接字符串?

Posted

技术标签:

【中文标题】对控制器进行单元测试 - 如何处理连接字符串?【英文标题】:Unit Testing a Controller - How Do I Handle the Connection String? 【发布时间】:2016-04-04 18:17:10 【问题描述】:

我可以让它发挥作用,但我想知道最佳做法是什么以及为什么。我有一个控制器、一个模型和一个存储库,现在我想对控制器进行单元测试。我只是在写一个简单的测试来确保返回正确的视图。

这是我在控制器中的方法:

    public ActionResult Selections(SelectionsViewModel model)
    
        for (int i = 0; i < model.Sends.Count; i++)
        
            Send send = new Send(new SendService(new Database().GetConnectionString()))
            
                SendID = model.Sends[i].SendID,
                Title = model.Sends[i].Title,
                Subject = model.Sends[i].Subject,
                SentDate = model.Sends[i].SentDate,
                TimesViewed = model.Sends[i].TimesViewed,
                Include = model.Sends[i].Include,
                Exclude = model.Sends[i].Exclude
            ;
            send.UpdateIncludeExclude();
        

        return View(model);
    

这是通过我的 SendService 构造函数发送的 Database 类中的 GetConnectionString() 方法。

    public string GetConnectionString()
    
        return System.Configuration.ConfigurationManager.ConnectionStrings["DEVConnectionString"].ToString();
    

最后,我的单元测试:

    [Test]
    public void TestAssignmentSelections()
    
        var obj = new AssignmentController();

        var actResult = obj.Selections() as ViewResult;

        NUnit.Framework.Assert.That(actResult.ViewName, Is.EqualTo("Selections"));
    

现在,我的单元测试失败了,我明白了原因。我的单元测试项目无法访问我正在测试的连接字符串所在的项目的 web.config。

我已经进行了一些研究,显然只是将 web.config 添加到我的单元测试项目中并将连接字符串也放入其中即可使其正常工作.. 但这似乎是一个 hack。

解决此问题的最佳方法是什么?有没有另一种方法来编写我的代码来适应这个?

【问题讨论】:

【参考方案1】:

您想让您的控制器单元可测试吗?不要这样做。

new SendService(

使用此代码,您可以对具体的服务实现和数据访问代码实现进行硬编码。在您的单元测试中,您不应该真正访问数据库中的数据。相反,您应该提供一个模拟数据访问实现。

接口来了,你需要为你的SendService创建一个接口。

public interface ISendService
 
  void SomeMethod();

现在您的SendService 将成为此接口的具体实现

public class SendService : ISendService

   public void SomeMethod()
   
     // Do something
   

现在更新您的控制器,使其具有一个构造函数,我们将在其中注入 ISendService 的实现。

public class YourController : Controller

   private ISendService sendService;
   public YourController(ISendService sendService)
   
     this.sendService = sendService;
         
   public ActionResult YourActionMethod()
   
    // use this.sendService.SomeMethod();
   

并且你可以使用一些依赖注入框架来告诉 MVC 框架在代码运行时使用哪个接口实现。如果您使用的是 MVC6,它有一个可以使用的内置依赖注入提供程序。因此,转到您的 Startup 类并在您的 ConfigureServices 方法中,您可以将接口映射到具体实现。

public class Startup

  public void ConfigureServices(IServiceCollection services)
  
     services.AddTransient<ISendService, SendService>();
  

如果您使用的是先前版本的 MVC,您可以考虑使用 Unity、Ninject 等 DI 框架。您可以稍后为您的数据访问/服务层执行相同的方法。即:为数据访问创建一个接口并将其注入到您的 SendService。

public Interface IDataAccess

  string GetName(int id);

以及使用您的特定数据访问代码/ORM 的实现

public class EFDataAccess : IDataAccess

  public string GetName(int id)
  
     // return a string from db using EF
  

所以现在你的服务类将是

public class SendService : ISendService

  private IDataAccess dataAccess;
  public SendService(IDataAccess dataAccess)
  
    this.dataAccess=dataAccess;
   
  // to do : Implement methods of your ISendService interface. 
  // you may use this.dataAccess in those methods as needed.

在您的单元测试中,您可以创建接口的模拟实现,它返回静态数据而不是访问数据库。

例如,如果您使用的是 Moq 模拟框架,则可以这样做。

var m = new Mock<IDataAccess>();
var m.Setup(s=>s.GetName(It.IsAny<int>())).Returns("Test");
var s = new SendService(m);
var result= s.SomeMethod();

【讨论】:

这很有帮助,我想我需要做出一些改变。谢谢。

以上是关于对控制器进行单元测试 - 如何处理连接字符串?的主要内容,如果未能解决你的问题,请参考以下文章

当循环中调用了异步函数时,Node.JS 将如何处理循环控制?

如何处理外部dll中的连接字符串?

如何对使用 cookie 的 socket.io 中间件进行单元测试

如何处理 Play 1.X 中的数据库连接错误?

如何处理 Cassandra 丢失的连接?

单元测试时如何处理 API 调用速率限制?