【问题标题】:Build model in unit test在单元测试中构建模型
【发布时间】:2015-07-21 16:42:48
【问题描述】:

只是想要一些指导,我将如何对以下操作进行单元测试:

public ActionResult Index()
{
    var model = _resolver.GetService<ISignUpViewModel>();
    model.Location =  _resolver.GetService<ILocations>().getLocations(string area);
    return PartialView("Login", model);
}


private IDependencyResolver _resolverMock;

[TestMethod]
public void SignUpTest()
{
    var _resolverMock = new Mock<IDependencyResolver>();
    var ctrl = new HomeController(_resolverMock.Object);
    var signUpMock = new Mock<ISignUpViewModel>();
    var LocationsMock = new Mock<ILocations>();

    _resolverMock.Setup(m => m.GetService(It.IsAny<Type>())).Returns(
             signUpMock.Object);

   _resolverMock.Setup(m => m.GetService(It.IsAny<Type>())).Returns(
             LocationsMock.Object);


    DependencyResolver.SetResolver(resolverMock.Object);

    ctrl.Index();
    ctrl.ViewData.Model = signUpMock;
}

如何在单元测试中建立模型?

另外,我如何从解析器调用 getLocations 方法?

不确定如何执行此操作?

【问题讨论】:

  • getLocations 返回一个 IEnumerable
  • 您的控制器依赖于 ISignUpViewModelFactory,它具有创建方法 public ISignUpViewModel Create(string area) {}。我会将此接口传递给控制器​​构造函数并在您的操作方法中调用它。就目前而言,您正在使用 servicelocator _resolver,您是对的,它们是要测试的 PITA。
  • 你能给我一个代码中的例子吗?不太关注你。

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


【解决方案1】:

根据您的代码,您的家庭控制器不依赖于 IDependencyResolver。它实际上依赖于ISignUpViewModel。因此,您可以将其中一个传递给构造函数或 ISignUpViewModelFactory。所以我会将模型解析重构为工厂类:

public class HomeController {

    private readonly ISignUpViewModelFactory _modelFactory;

    public HomeController(ISignUpViewModelFactory modelFactory){
          _modelFactory = modelFactory;
    }

    public ActionResult Index()
    {
        return PartialView("Login", _modelFactory.Create(area));
    }
}


public interface ISignUpViewModelFactory {
  ISignUpViewModel Create(string area);
}

public class ProductionSignUpViewModelFactory : ISignUpViewModelFactory
{
    public ISignUpViewModel Create(string area){
       // create and return your models here
       // if you still have to use a service locator in this factory then
       // refactor some more until you get these dependencies out in the open.
    }
}

public class MockSignUpViewModelFactory : ISignUpViewModelFactory
{
    public ISignUpViewModel Create(string area){
       return new SignUpViewModel();
    }
}

在生产环境中,您的IOC 注入了ProductionSignUpViewModelFactory 的一个实例。在测试中,你传入一个MockSignUpViewModelFactory

不同之处在于,使用此方法您仅测试操作(即单元),而使用当前方法您正在测试操作和您的服务定位器_resolver 加上您已经模糊了实际依赖关系,即ISignUpViewModel 不是IDependencyResolver.

根据ProductionSignUpViewModelFactory 中的cmets,如果您必须将IDependencyResolver 注入该工厂以使其工作,那么您可能仍然做错了。然后,您需要查看 ProductionSignUpViewModelFactory 需要什么来创建 ISignUpViewModel 的实例并将其注入而不是 IDependencyResolver

最终,您将到达依赖链的顶端,您会发现单元测试变得非常非常容易。如果您首先构建测试,即 TDD,那就更容易了。 :)

【讨论】:

  • 因此,如果我有另一个具有类似设置的操作,即创建一个新用户,我将不得不有另一个构造函数,它采用 INewUser 和另一个 MockNewUserFactory。目的是让 HomeController 变得很薄?
  • 是的,很有可能。您可能会发现您的单个控制器实际上是 3 或 4 个控制器。我知道这听起来像是更多的工作(实际上不是),但从长远来看要容易得多。使用此方法您将获得额外的课程,但恕我直言,这是唯一的方法,特别是用于测试,如果您使用 Visual Studio 生成脚手架,那么它几乎不需要额外的工作。目的不是让它“变薄”,而只是自我描述和可测试。那些隐藏的部门模糊了类的含义。我必须在里面查看它需要什么,但签名应该足够了。
  • 我会试一试,让你知道我的进展如何。谢谢
猜你喜欢
  • 1970-01-01
  • 2019-06-21
  • 2015-06-24
  • 2017-12-19
  • 2010-12-26
  • 1970-01-01
  • 2012-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多