【问题标题】:How do you Moq a .resx that lives in App_GlobalResource?你如何起订一个生活在 App_GlobalResource 中的 .resx?
【发布时间】:2012-10-12 08:38:10
【问题描述】:

我正在为 MVC3 Web 项目中的控制器编写单元测试,但是当我的测试尝试访问这样的资源时会抛出异常:

return Index(Resources.Strings.MyStringResource);

资源是一个名为Strings 的.resx 文件。

我正在使用 Moq 库来实现 HttpContextBase 功能的单元测试,所以我想知道如何使用 Moq 库来访问 App_GlobalResource。

任何帮助或指点将不胜感激!

【问题讨论】:

    标签: .net asp.net-mvc-3 unit-testing moq app-globalresources


    【解决方案1】:

    你不能,至少不能直接。从资源 (.resx) 文件生成的强类型类公开静态方法,而不是实例方法。

    正因为如此,它们不能实现接口方法,也不是虚拟的; Moq 要求至少满足其中一个条件才能创建模拟。

    要解决这个问题,您可以创建一个抽象,就像其他任何东西一样:

    public interface IResources
    {
        string MyStringResource { get; }
    }
    

    您可以将它的实现传递(或注入)到您的控制器中,然后将其传递给您的Index 方法。该实现可能如下所示:

    public class ResourcesWrapper : IResources
    {
        public string MyStringResource 
        { 
            get 
            { 
                return Resources.Strings.MyStringResource; 
            } 
        }
    }
    

    然后,当您进行测试时,您可以使用 Moq 创建 IResources 接口的模拟,并将其传递给您的控制器,如下所示:

    // Create the mock.
    var mock = new Mock<IResources>();
    
    // Setup the property.
    mock.SetupProperty(m => m.MyStringResource, "My Mocked Value");
    
    // Pass the object somewhere for use.
    Assert.AreEqual(mock.Object.MyStringResource, "My Mocked Value");
    

    【讨论】:

    • 嗨 Casper,我刚刚尝试实施您的答案,但失败了,抛出 IOException: "Could not load file or assembly 'App_GlobalResources' or one of its dependencies. The system cannot find the file specified.":"App_GlobalResources" 您能提供更多提示吗?
    • @MartinBlake 这听起来像是一个单独的问题。虽然ResourcesWrapper 实现IResources 的类应该存在于嵌入资源的同一个程序集中;那些类型化的包装器被标记为内部的。'
    • 在下面查看我的解决方案,我会将您的答案标记为已接受的答案,因为没有它我将无处可去!感谢您的帮助!
    【解决方案2】:

    所以,在执行casperOne的回答后,我又遇到了一个错误:

    我收到了一封IOException 说明:

    "Could not load file or assembly 'App_GlobalResources' or one of its dependencies. The system cannot find the file specified.":"App_GlobalResources"

    Scott Allen为此问题提供了reason and inherent solution。

    所以我所做的是在我的网络项目中名为“TResources”的新文件夹中创建了一个新资源文件,命名为“TResources”纯粹是因为它是一个仅用于测试目的的资源文件夹(聪明,嗯?)

    然后我将ResourcesWrapper 类的属性更改为返回TResources.Strings.MyStringResource 而不是Resources.Strings.MyStringResource。

    注意:IResources接口中的属性不能是只读的,和设置mock对象时一样,如果属性是read -only 会失败,因为无法设置值。

    因此,IResources 应该看起来像这样:

    public interface IResources
    {
        string MyStringResource { get; set; }
    }
    

    ResourcesWrapper 应该像这样实现IResources:

    public class ResourcesWrapper : IResources
    {
        public string MyStringResource 
        { 
            get 
            { 
                return TResources.Strings.MyStringResource; 
            } 
            set
            {
                //do nothing
            }
        }
    }
    

    这样您就可以在单元测试中成功模拟,如下所示:

    var mock = new Mock<IResources>();
    mock.SetupProperty(m => m.MyStringResource, "");
    

    注意:您不必在上述方法的initialValue 参数中指定任何内容,因为该属性将返回从Strings.resx 检索到的值。

    我的问题到此结束,我希望这对互联网领域的其他人有所帮助!

    【讨论】:

    • 为什么要在界面中添加一个setter? Moq 绝对允许只读属性设置值,但您必须调用 SetProperty。此外,这是一个单独的问题和答案,它没有直接解决问题,如发布的那样。
    • 以为我可能把它贴错了地方。关于它应该去哪里的任何提示?我已经将它们创建为只读属性,但它抛出了一个错误,所以我只是在那里推了一个二传手。这只是一个演示项目,所以我并不担心。
    【解决方案3】:

    This response 到 similar Stack Overflow question by Darin Dimitrov 提供了一种更简单的方法。全部都是关于修改 Resources.resx 文件属性以支持单元测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 2016-01-07
      • 2012-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多