【问题标题】:Mocking non virtual posted model properties and method模拟非虚拟发布模型属性和方法
【发布时间】:2018-02-20 18:38:58
【问题描述】:

我将人模型类定义为:

public class PersonModel
{
    public bool SelectionSubmitted = false;

    public bool ShowValidationSummary = false;

    public string Name;

    public string Get()
    {            
        //actual implementation return some value from the db
        return string.Empty;
    }
}

控制器实现如下:

class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index(PersonModel model)
    {
        if (model.SelectionSubmitted && !ValidateSelections(model))
        {
            model.ShowValidationSummary = true;
        }

        return View("Index", model.Get());
    }

    private bool ValidateSelections(PersonModel model)
    {
        if(model.Name == "")
        {
            ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
        }
        return ModelState.IsValid;
    }       
}

测试类和方法定义为:

[TestClass]
public class ChildWithoutPlacementControllerTest
{
    private readonly Mock<PersonModel> _mockPersonModel;

    public ChildWithoutPlacementControllerTest()
    {
        _mockPersonModel = new Mock<PersonModel>();
    }

    [TestMethod]
    public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown()
    {
        //Arrange
        HomeController controller = new HomeController();
        _mockPersonModel.Setup(x => x.Get()).Returns(It.IsAny<string>());
        _mockPersonModel.SetupGet(x => x.Name).Returns(string.Empty);
        _mockPersonModel.SetupGet(x => x.SelectionSubmitted).Returns(true);

        //Act
        controller.Index(_mockPersonModel.Object);

        //Assert
        var isShowSummarySetToTrue = _mockPersonModel.Object.ShowValidationSummary;
        Assert.IsTrue(isShowSummarySetToTrue);
    }
}

我想要实现的是将SelectionSubmittedName属性分别模拟为truestring.Empty以及SetupGet类的Get方法,并检查测试是否返回对象已将 ShowValidationSummary 设置为 true

但是,我发现我无法设置非虚拟属性名称。

我做错了什么还是有什么方法可以在不更改实现代码的情况下做到这一点?

【问题讨论】:

  • @nkosi 我猜你发布了一个答案,但现在找不到。它确实帮助这种方法对我很有效。非常感谢!!。
  • 我发现了一个问题并正在审查该问题。你真的应该考虑重构你的代码,让它更容易测试。
  • 是的,我正在研究如何使它更可测试。 :)

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


【解决方案1】:

我做错了什么

这似乎是XY problem

有什么方法可以在不改变实现代码的情况下做到这一点

在这种情况下确实不需要起订量。您可以使用继承来制作要在测试中使用的假模型。假模型将覆盖与数据库紧密耦合的方法。 (稍后会详细介绍)

​​>
public class FakePerson : PersonModel {
    public new string Get() {
        return string.Empty; //Not calling the base Get
    }
}

然后可以重构测试以使用假模型并按预期完成。

[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
    //Arrange
    var fakePersonModel = new FakePerson() {
        Name = string.Empty,
        SelectionSubmitted = true
    };
    var controller = new HomeController();

    //Act
    controller.Index(fakePersonModel);

    //Assert
    var isShowSummarySetToTrue = fakePersonModel.ShowValidationSummary;
    Assert.IsTrue(isShowSummarySetToTrue);
}

除此之外,如果实际的 Get 实现按照您在此处所述进行,那么您的模型似乎可以做很多事情

实际的实现从数据库中返回一些值

考虑将该功能重构为服务(单一职责原则/关注点分离)

public interface IPersonModelService {
    string Get(PersonModel person);
}

public class PersonModelService : IPersonModelService {

    public string Get(PersonModel person) {
        //actual implementation return some value from the db
    }
}

并尽可能保持模型精简。还可以考虑将这些公共字段重构为公共属性。

public class PersonModel {
    public bool SelectionSubmitted { get; set; }

    public bool ShowValidationSummary { get; set; }

    public string Name { get; set; }

}

控制器将依赖于服务抽象

class HomeController : Controller {
    private IPersonModelService service;

    public HomeController(IPersonModelService service) {
        this.service = service;
    }

    [HttpGet]
    public ActionResult Index(PersonModel model) {
        if (model.SelectionSubmitted && !ValidateSelections(model)) {
            model.ShowValidationSummary = true;
        }

        return View("Index", service.Get(model));
    }

    private bool ValidateSelections(PersonModel model) {
        if (model.Name == "") {
            ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
        }
        return ModelState.IsValid;
    }
}

现在可以单独完成测试。

[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
    //Arrange
    var model = new PersonModel() {
        Name = string.Empty,
        SelectionSubmitted = true
    };

    var serviceMock = new Mock<IPersonModelService>();
    serviceMock.Setup(_ => _.Get(It.IsAny<PersonModel>())).Returns(string.Empty);
    var controller = new HomeController(serviceMock.Object);

    //Act
    controller.Index(model);

    //Assert
    var isShowSummarySetToTrue = model.ShowValidationSummary;
    Assert.IsTrue(isShowSummarySetToTrue);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    相关资源
    最近更新 更多