c# - 当被测系统使用外部静态依赖项时,如何编写单元测试?

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

我正在为我的方法创建单元测试

[Authorize]
[HttpPost]
public async Task<JsonResult> UpdateDisplayName(string displayname)
{
    bool status = _myProfileService.UpdateDisplayName(displayname, SessionAdapter.Instance._userDetailsViewModel.id);

    if (status)
        SessionAdapter.Instance._userDetailsViewModel.display_name = displayname;

    return Json(new { status = status, displayname = displayname }, JsonRequestBehavior.AllowGet);
}

我的测试方法是

[TestMethod]
public async Task UpdateDisplayName_Test()
{
    //Arrange
    var controller = new HomeController(userServiceMock.Object, myProfileServiceMock.Object);

    string displayName = "display";
    const string expected = "{ status = False, displayname = display }";
    myProfileServiceMock.Setup(m => m.UpdateDisplayName(It.IsAny<string>(), 1)).Returns(false);

    //var controllerContextMock = new Mock<ControllerContext>();

    //Act
    var result = await controller.UpdateDisplayName(displayName) as JsonResult;

    //Assert
    Assert.AreEqual(expected, result.Data.ToString());
}

我的 session 信息类如下,我在 session 适配器中使用该类

  public  class SessionInfo
    {
        public string Id { set; get; }
        public string Email { set; get; }
        public string UserName { set; get; }
        public UserDetailsViewModel _userDetailsViewModel { set; get; }
        public string permission { set; get; }

        //public string organization { set; get; }
        public OrganizationViewModels Organization { set; get; }
        public List<UserTeamModels> teams { set; get; }
        public string status { set; get; }
        public string role { set; get; }
        public List<string> roles { set; get; }
    }

我无法实例化SessionAdapter。我如何对其进行单元测试?

我的界面和 sessionadapter 类如下所示

public interface ISessionAdapterService
    {
        void Clear();
        void Abandon();
        bool DoesSessionExists { get; }
        SessionInfo Instance { get; set; } 
    }


    public class SessionAdapterService : ISessionAdapterService
    {
        private string sessionKey = "sessionInfoKey";

        public bool DoesSessionExists
        {
            get
            {
                return HttpContext.Current.Session[sessionKey] == null ? false : true;
            }

        }

        public SessionInfo Instance
        {
            get
            {
                return HttpContext.Current.Session[sessionKey] == null ? null : (SessionInfo)HttpContext.Current.Session[sessionKey];
            }
            set
            {
                HttpContext.Current.Session[sessionKey] = value;
            }
        }



        public void Abandon()
        {

            HttpContext.Current.Session.Abandon();
            HttpContext.Current.Session[sessionKey] = null;
        }

        public void Clear()
        {
            HttpContext.Current.Session.Clear();

        }
    }

我的测试用例与下面的答案相同

[TestMethod]
public async Task UpdateDisplayName_Test() {
    //Arrange
    var mySessionAdaptorService = new Mock<ISessionAdaptorService>();

    var controller = new HomeController(userServiceMock.Object, myProfileServiceMock.Object, mySessionAdaptorService.Object);

    var displayName = "display";
    var status = false;
    var id = 1;
    myProfileServiceMock.Setup(m => m.UpdateDisplayName(It.IsAny<string>(), id)).Returns(status);
    mySessionAdaptorService.Setup(m => m.Instance.Id).Returns(id);

    //Act
    var result = await controller.UpdateDisplayName(displayName) as JsonResult;

    //Assert
    Assert.IsNotNull(result); 
}

代码更新。请找到我用于 SessionAdapter 类和 ISessionAdapter 接口(interface)以及实现的以下代码。请给出您的建议,这是正确的方法。

     public interface ISessionInfo
        {
            string Id { set; get; }
            string Email { set; get; }
            string UserName { set; get; }
            UserDetailsViewModel _userDetailsViewModel { set; get; }
            string permission { set; get; }

            OrganizationViewModels Organization { set; get; }
            List<UserTeamModels> teams { set; get; }
            string status { set; get; }
            string role { set; get; }
            List<string> roles { set; get; }
        }

        public  class SessionInfo : ISessionInfo
        {
            public string Id { set; get; }
            public string Email { set; get; }
            public string UserName { set; get; }
            public UserDetailsViewModel _userDetailsViewModel { set; get; }
            public string permission { set; get; }

            //public string organization { set; get; }
            public OrganizationViewModels Organization { set; get; }
            public List<UserTeamModels> teams { set; get; }
            public string status { set; get; }
            public string role { set; get; }
            public List<string> roles { set; get; }
        }

 public interface ISessionAdapter
    {
        void Clear();
        void Abandon();
        bool DoesSessionExists { get; }
        ISessionInfo Instance { get; set; }

    }


    public class SessionAdapter : ISessionAdapter
    {
        private string sessionKey = "sessionInfoKey";

        public bool DoesSessionExists
        {
            get
            {
                return HttpContext.Current.Session[sessionKey] == null ? false : true;
            }
        }

        public ISessionInfo Instance
        {
            get
            {
                return HttpContext.Current.Session[sessionKey] == null ? null : (SessionInfo)HttpContext.Current.Session[sessionKey];
            }
            set
            {
                HttpContext.Current.Session[sessionKey] = value;
            }
        }

        public void Abandon()
        {

            HttpContext.Current.Session.Abandon();
            HttpContext.Current.Session[sessionKey] = null;
        }

        public void Clear()
        {
            HttpContext.Current.Session.Clear();

        }

最佳答案

抽象外部依赖并将其注入(inject)到 Controller 中。只要它保持静态,就没有太多其他测试可做

抽象可以是这样的

public interface ISessionAdapterService {
    int Id { get; }
    string DisplayName { get; set; }
}

具有以下实现。

public class SessionAdapterService : ISessionAdapterService {
    public string DisplayName { 
        get { return SessionAdapter.Instance._userDetailsViewModel.display_name; } 
        set { SessionAdapter.Instance._userDetailsViewModel.display_name = value; } 
    }

    public int Id {
        get { return SessionAdapter.Instance._userDetailsViewModel.id; }
    }
}

Controller 需要使用抽象作为依赖

[Authorize]
[HttpPost]
public async Task<JsonResult> UpdateDisplayName(string displayname) {
    bool status = _myProfileService.UpdateDisplayName(displayname, sessionAdapterService.Id);

    if (status)
        sessionAdapterService.DisplayName = displayname;

    return Json(new { status = status, displayname = displayname }, JsonRequestBehavior.AllowGet);
}

假设sessionAdapterService是一个注入(inject)的ISessionAdapterService

单元测试现在可以模拟外部依赖项并将其注入(inject) Controller 。

[TestMethod]
public async Task UpdateDisplayName_Test() {
    //Arrange
    var mySessionAdaptorService = new Mock<ISessionAdaptorService>();

    var controller = new HomeController(userServiceMock.Object, myProfileServiceMock.Object, mySessionAdaptorService.Object);

    var displayName = "display";
    var status = false;
    var id = 1;
    myProfileServiceMock.Setup(m => m.UpdateDisplayName(It.IsAny<string>(), id)).Returns(status);
    mySessionAdaptorService.Setup(m => m.Id).Returns(id);

    //Act
    var result = await controller.UpdateDisplayName(displayName) as JsonResult;

    //Assert
    Assert.IsNotNull(result);
    dynamic data = result.Data;
    Assert.IsNotNull(data);
    Assert.AreEqual(displayName, (string)data.displayname);
    Assert.AreEqual(status, (bool)data.status);
}

更新。

根据您的评论更新了 SessionInfo 类的抽象

public interface ISessionInfo {
    string Id { set; get; }
    string Email { set; get; }
    string UserName { set; get; }
    UserDetailsViewModel _userDetailsViewModel { set; get; }
    string permission { set; get; }

    OrganizationViewModels Organization { set; get; }
    List<UserTeamModels> teams { set; get; }
    string status { set; get; }
    string role { set; get; }
    List<string> roles { set; get; }
}

public class SessionInfo : ISessionInfo { ... }

public interface ISessionAdapterService {
    void Clear();
    void Abandon();
    bool DoesSessionExists { get; }
    ISessionInfo Instance { get; set; } 
}

我还建议您检查一下模型设计。它非常脆弱,并迫使 session 信息类与实现问题紧密耦合。

关于c# - 当被测系统使用外部静态依赖项时,如何编写单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41877657/

相关文章:

c# - 包含相关对象子集的属性

c# - 如何使用 C# 显示具有两种状态的数据

c# - 向下转换通用元素类型

c# - Entity Framework 过滤器嵌套集合

c# - 使用未定义的 Dictionary<Type, Type> 作为方法的输入

c# - "Security Models"有多少种?

asp.net-mvc - .net mvc 仅在具有值时才在 View 中显示字段

c# - 在 JQuery 和 MVC 中使用 JSON 的问题

c# - 将修改后的 WordprocessingDocument 保存到新文件

c# - 创建项目依赖项并添加对项目的引用仍然导致 "The type or namespace name could not be found"