如何注入与 Moq 一起使用的模拟程序集

How to inject a mock Assembly for use with Moq

我的控制器中有一个方法 returns 将当前正在执行的程序集中的数据属性化为局部视图。

在这个例子中,我只是提取标题,但我需要用它做更多的事情。

控制器:

    var title = "";

    var asm = Assembly.GetExecutingAssembly();
    var attrs = asm.GetCustomAttributes(typeof(AssemblyTitleAttribute));
    var titleAttr = (AssemblyTitleAttribute)attributes[0];

    title = titleAttr.Title;

    return PartialView("_Build", title);

在 Moq 中编写单元测试时,我需要找到一种方法将 Assembly 属性注入到 mock 中,以便在我 运行 控制器测试时验证是否生成了正确的属性,然后用我的断言做 x 或 y。

单元测试:

    //Arrange
    //The magic I need to happen.

    //Act
    var controller = GetController();
    var result = controller.MyMethod() as PartialViewResult;
    var title = result.Model;

    //Assert
    Assert.AreEqual("Title", title); //currently static, need to verify against a mock

我知道这是一组极其简单的代码,但此时我只需要概念证明。

有没有什么好的方法来创建一个假的Assembly? 我需要使用 System.Reflection.Emit 吗?

由于这是静态类型信息,因此每次构建只需评估一次。考虑在应用程序启动时使用此信息填充静态集合(例如字典)。那时你可以将你的字典注入控制器,或者你可以修改你的配置来访问字典并只注入你需要的值。事实上,如果这不会失控,最好只使用 "build" 或 "title" 之类的命名字符串实例,甚至不必将它们放在集合中。

除非逻辑可以包含在您的单元测试直接调用的静态方法中,否则我不会为尝试围绕解析程序集属性进行单元测试的麻烦而烦恼。从 Web 应用程序的角度来看,最好将此类内容留在控制器之外;引导程序层是一个更合适的位置。

您可以创建一个虚拟方法,例如GetCustomAttributes 提供给定类型的属性。然后在您的测试项目中,一个 testable class 将从控制器 class 派生并覆盖此方法。

Home Controller:

private IDependency _someDependency;

public HomeController(IDependency someDependency)
{
    _someDependency = someDependency;
}

public ActionResult MyMethod()
{
    var title = "";
    var version = "";

    IEnumerable<Attribute> attributes = GetCustomAttributes(typeof(AssemblyVersionAttribute)).ToList();
    AssemblyVersionAttribute verAttr = attributes.OfType<AssemblyVersionAttribute>().FirstOrDefault();
    if (verAttr != null) version = verAttr.Version;

    attributes = GetCustomAttributes(typeof(AssemblyTitleAttribute)).ToList();
    AssemblyTitleAttribute titleAttr = attributes.OfType<AssemblyTitleAttribute>().FirstOrDefault();
    if (titleAttr != null) title = titleAttr.Title;

    return PartialView("_Build", title + version);
}

public virtual IEnumerable<Attribute> GetCustomAttributes(Type attributeType)
{
    var asm = Assembly.GetExecutingAssembly();
    var attrs = asm.GetCustomAttributes(attributeType);
    return attrs;
} 

Test:

[TestClass]
public class MyMethodTest
{
    [TestMethod]
    public void MyMethod_WhenCalled_PartialViewIsReturned()
    {
        // Arrange
        // The magic I need to happen.

        Mock<IDependency> dependencyStub = new Mock<IDependency>();
        // dependencyStub.Setup(...).Returns(...);
        var controller = new TestableHomeController(dependencyStub.Object)
        {
            UseFakeAttributes = true
        };

        // Act
        var result = controller.MyMethod() as PartialViewResult;

        // Assert
        var model = result.Model;
        Assert.AreEqual("MyFakeTitle1.0.0.0", model); // currently static, need to verify against a mock
    }

    private class TestableHomeController : HomeController
    {
        public bool UseFakeAttributes { get; set; }

        public TestableHomeController(IDependency someDependency)
            :base(someDependency)
        { }

        public override IEnumerable<Attribute> GetCustomAttributes(Type attributeType)
        {
            return UseFakeAttributes
                ? new List<Attribute>
                    {
                        new AssemblyTitleAttribute("MyFakeTitle"),
                        new AssemblyVersionAttribute("1.0.0.0"),
                        new AssemblyDescriptionAttribute("Assembly fake description")
                        // next attributes ...
                    }.Where(a => a.GetType() == attributeType)
                : base.GetCustomAttributes(attributeType);
        }
    }
}