代码之家  ›  专栏  ›  技术社区  ›  gin93r

如何对依赖于HttpContext.GetGlobalResourceObject的类进行单元测试?

  •  3
  • gin93r  · 技术社区  · 6 年前

    我正在尝试向webforms项目添加测试。有一个静态方法从资源文件中获取行。我要测试的一个类依赖于从资源文件中获取文本。

    public static class MyStaticClass {
        public static string getText(String name)
        {
            String s = HttpContext.GetGlobalResourceObject("MyResources", name).ToString();
            return s;
        }
    }
    
    public class ClassUnderTest
    {
        // returns: "Hey it's my text"
        private string _eg = MyStaticClass.getText("label_in_resources.resx_file")
    }
    
    class UnitTests
    {
        [Test]
        public void TestMyClass()
        {
            ClassUnderTest _cut = new ClassUnderTest();
            // errors out because ClassUnderTest utilizes getText
            // which requires HttpContext.GetGlobalResourceObject
            // ... other stuff
        }
    }
    

    注:这些都是简单的例子。

    问题是我得到一个测试失败的消息:

    消息:system.nullreferenceexception:对象引用未设置为对象的实例。

    经过我的侦查,我确定这是因为 HttpContext 在这些测试期间为空。

    我看过不少关于嘲笑的帖子 请求上下文 但我不认为我完全理解他们到底在做什么,因为他们通常处理的是mvc而不是webforms。他们中的大多数仍然使用 HttpContextBase 和/或 HttpContextWrapper 但我还是不确定如何实施。

    而且-我不是直接测试 getText 方法。我知道它有效。我正在测试一个使用它的类。会嘲笑 请求上下文 即使是在这种情况下帮忙?

    我确实意识到这是单元测试/集成测试的混合,所以如果这不是最好的方法,我会全神贯注…或…眼睛更确切。

    编辑

    现在,我修改了 获得文本 方法返回键(名称),如果 HttpContext.GetGlobalResourceObject 是空的。然后我更新了测试以期望密钥而不是值。这不太理想,但它可以让我继续。如果有更好的办法,请告诉我。

    public static class MyStaticClass {
        public static string getText(String name)
        {
            String s = HttpContext.GetGlobalResourceObject("MyResources", name);
            return s != null ? s.ToString() : name;
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   TyCobb    6 年前

    假货的原始答案(有关消除静电的处理,请参见下文)

    所以有一个警告,我完全忘记了,直到我试图这样做。我敢肯定,Fakes仍然需要企业版的VS。我不知道是否有办法让它与Nunit一起工作,但当你无法更改代码时,有时你不得不处理它。

    下面是一个给你的静态方法加上微光的例子。你还不需要担心httpcontext,因为你没有直接使用它。相反,你可以给你的 getText(string) 方法。

    实际业务项目

    namespace FakesExample
    {
        public class MyStaticClass
        {
            public static string GetText(string name)
            {
                throw new NullReferenceException();
            }
        }
    }
    

    你的单元测试项目

    using System;
    using Microsoft.QualityTools.Testing.Fakes;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace FakesExampleTests
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
                using (ShimsContext.Create())
                {
                    FakesExample.Fakes.ShimMyStaticClass.GetTextString = (s) =>
                    {
                        return "Go away null reference";
                    };
    
                    Console.WriteLine(FakesExample.MyStaticClass.GetText("foo"));
                }
            }
        }
    }
    

    我真的运行了这个,所以我知道它是有效的。即使这样 GetText 调用时总是抛出一个nullreferenceexception,它的填充程序返回我们自己的自定义消息。

    您可能必须生成一个visual studio测试项目。

    在您的单元测试项目中,右键单击您的引用并说“add fakes”。它将为您的装配生成所有的垫片和短柱。


    静电消除过程

    最好的解决办法是努力消除静电。你已经找到了一个不使用它们的主要原因。

    下面是如何删除静态和对httpcontext的依赖

    public interface IResourceRepository
    {
        string Get(string name);
    }
    
    public class HttpContextResourceRepository : IResourceRepository
    {
        public string Get(string name)
        {
            return HttpContext.GetGlobalResourceObject("MyResources", name).ToString();
        }
    }
    
    public class MyFormerStaticClass
    {
        IResourceRepository _resourceRepository;
    
        public MyFormerStaticClass(IResourceRepository resourceRepository)
        {
            _resourceRepository = resourceRepository;
        }
    
        public string GetText(string name)
        {
            return _resourceRepository.Get(name);
        }
    }
    

    然后,我将利用依赖注入来处理 HttpContextResourceRepository MyStaticClass (可能也应该是接口)在实际的业务代码中。

    对于单元测试,我将模拟实现

    [TestFixture]
    public class UnitTest1
    {
        [Test]
        public void TestMethod1()
        {
            var repoMock = new Mock<IResourceRepository>();
            repoMock.Setup(repository => repository.Get("foo")).Returns("My Resource Value");
    
            var formerStatic = new MyFormerStaticClass(repoMock.Object);
            Console.WriteLine(formerStatic.GetText("foo"));
        }
    }
    

    按照这条路线,您可以创建任意数量的iresourcerepository实现,并随时交换它们。