当被测系统利用外部静态依赖时,如何编写单元测试?

时间:2017-01-26 15:59:41

标签: 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());
}

我的会话信息类如下,我在会话适配器中使用的这个类

  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接口的以下代码以及implementation.please以正确的方式给出您的建议。

     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();

        }

1 个答案:

答案 0 :(得分:4)

抽象外部依赖并将其注入控制器。只要它保持静止,那么在测试方面没有什么可做的

抽象可以看起来像这样

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是注入ISessionAdapterService

单元测试现在可以模拟外部依赖关系并将其注入控制器。

[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; } 
}

我还建议你检查模型设计。它非常脆弱,迫使会话信息类与实现问题紧密结合。