c# - 对使用 Windows 身份验证的 Controller 进行单元测试

标签 c# asp.net asp.net-mvc unit-testing

--------请参阅下面的更新,因为我现在已经设置了依赖注入(inject)和 MOQ 模拟框架的使用。我仍然想拆分我的存储库,这样它就不会直接依赖于在同一函数中拉取 windowsUser。


我的 Intranet 站点中有一个 Web API,用于填充下拉列表。下拉列表后面的查询将 Windows 用户名作为参数返回列表。

我意识到我没有正确设置所有这些,因为我无法对其进行单元测试。我需要知道这个“应该”如何设置以允许单元测试,然后单元测试应该是什么样子。

附加信息:这是一个 ASP.NET MVC 5 应用程序。

界面

public interface ITestRepository
{
    HttpResponseMessage DropDownList();
}

存储库

public class ExampleRepository : IExampleRepository
{
    //Accessing the data through Entity Framework
    private MyDatabaseEntities db = new MyDatabaseEntities();

    public HttpResponseMessage DropDownList()
    {
        //Get the current windows user
        string windowsUser =  HttpContext.Current.User.Identity.Name;

        //Pass the parameter to a procedure running a select query
        var sourceQuery = (from p in db.spDropDownList(windowsUser)
                           select p).ToList();

        string result = JsonConvert.SerializeObject(sourceQuery);
        var response = new HttpResponseMessage();
        response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");

        return response;            
    }
}

Controller

public class ExampleController : ApiController
{
    private IExampleRepository _exampleRepository;

    public ExampleController()
    {
        _exampleRepository = new ExampleRepository();
    }

    [HttpGet]
    public HttpResponseMessage DropDownList()
    {
        try
        {
            return _exampleRepository.DropDownList();
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }
}

更新1

我已经根据 BartoszKP 的建议更新了我的 Controller 以显示依赖项注入(inject)。

更新 Controller

public class ExampleController : ApiController
{
    private IExampleRepository _exampleRepository;

    //Dependency Injection
    public ExampleController(IExampleRepository exampleRepository)
    {
        _exampleRepository = exampleRepository;
    }

    [HttpGet]
    public HttpResponseMessage DropDownList()
    {
        try
        {
            return _exampleRepository.DropDownList();
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }
}

更新2

我决定使用 MOQ 作为单元测试的模拟框架。我可以测试一些简单的东西,如下所示。 这将测试一个不带任何参数且不包含 windowsUser 部分的简单方法

[TestMethod]
public void ExampleOfAnotherTest()
{
    //Arrange
    var mockRepository = new Mock<IExampleRepository>();
    mockRepository
        .Setup(x => x.DropDownList())
        .Returns(new HttpResponseMessage(HttpStatusCode.OK));

    ExampleController controller = new ExampleController(mockRepository.Object);
    controller.Request = new HttpRequestMessage();
    controller.Configuration = new HttpConfiguration();

    //Act            
    var response = controller.DropDownList();

    //Assert
    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

我需要测试 DropDownList 方法的帮助(其中包含获取 windowsUser 的代码)。我需要有关如何分解此方法的建议。我知道这两个部分不应该采用相同的方法。我不知道如何安排拆分 windowsUser 变量。我意识到这确实应该作为参数引入,但我不知道如何实现。

最佳答案

您通常不对存储库进行单元测试(集成测试验证它们是否确实正确地将数据持久保存在数据库中) - 例如参见 this article on MSDN :

Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.

所以,让我们专注于仅测试 Controller 。

更改 Controller 以在其构造函数中采用 IExampleRepository 作为参数:

private IExampleRepository _exampleRepository;

public ExampleController(IExampleRepository exampleRepository)
{
    _exampleRepository = exampleRepository;
}

然后,在单元测试中,使用模拟框架之一(例如 RhinoMock)创建一个 stub ,其唯一目的是测试 Controller 。

[TestFixture]
public class ExampleTestFixture
{
    private IExampleRepository CreateRepositoryStub(fake data)
    {
        var exampleRepositoryStub = ...; // create the stub with a mocking framework

        // make the stub return given fake data

        return exampleRepositoryStub;
    }

    [Test]
    public void GivenX_WhenDropDownListIsRequested_ReturnsY()
    {
        // Arrange
        var exampleRepositoryStub = CreateRepositoryStub(X);
        var exampleController = new ExampleController(exampleRepositoryStub);

        // Act
        var result = exampleController.DropDownList();

        // Assert
        Assert.That(result, Is.Equal(Y));
    }  
}

这只是一个快速而肮脏的示例 - CreateRepositoryStub 方法当然应该提取到某个测试实用程序类。也许它应该返回一个流畅的界面,以使测试的 Arrange 部分在给定的内容上更具可读性。更像是:

// Arrange
var exampleController
    = GivenAController()
      .WithFakeData(X);

(当然,使用更好的名称来反射(reflect)您的业务逻辑)。


对于 ASP.NET MVC,框架需要知道如何构造 Controller 。幸运的是,ASP.NET 支持依赖注入(inject)范例,并且当 using MVC unity 时不需要无参数构造函数。 .


另外,请注意 Richard Szalay 的评论:

You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method

关于c# - 对使用 Windows 身份验证的 Controller 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34519238/

相关文章:

c# - 静态字段初始化在 C# 中如何工作?

c# - 使用 C# 替换 native .exe 中的字符串

asp.net - 是否可以将 asp.net 2012 Web 应用程序发布到 azure?

c# - 将多个 GridView 导出到多个 excel 选项卡(工作表)

c# - 开始文件下载时如何避免 IE7 中出现安全消息?

c# - 在 Action Filter onActionExecuting 上重定向时,请求的资源上存在错误 'Access-Control-Allow-Origin' header

c# - 使用 MVVM 模式验证 ViewModel 中绑定(bind)的 ObservableCollection

C# TPL : Possible to restart a failed Pipeline at an arbitrary step?

asp.net - 配置错误 : <compilation debug ="true" targetFramework ="4.0"> ASP. NET MVC3

c# - 系统.InvalidOperationException : The model item passed into the dictionary is of type