【问题标题】:how do you mock an xml for unit testing?你如何模拟一个用于单元测试的 xml?
【发布时间】:2013-11-13 00:39:17
【问题描述】:

我需要对这个 GetData 方法进行单元测试。

    public MessageResponse GetData(XmlElement requestElement)
    {   
        MessageResponse MsgResponse = new MessageResponse();


        if (requestElement.Attributes["employeeNo"] == null){
            MsgResponse.Messages = new List<string>();
            MsgResponse.Messages.Add("Attribute employeeNo is missing");
            MsgResponse.Error = true;
            return MsgResponse;
        }
        if (requestElement.Attributes["xmlEmployeeName"] == null){
            MsgResponse.Messages.Add("Attribute xmlEmployeeName is missing");
            MsgResponse.Error = true;
            return MsgResponse;
        }
        return MsgResponse;
    }

此方法需要一个 XmlElement 参数。我如何模拟它?在我的代码中,我首先创建了一个 xmlDocument,然后加载了 xml 文件。

            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFilePath);
            requestElement = doc.DocumentElement;

我要测试它,首先我需要创建一个没有employeeNo 的xml 文件,创建另一个没有名称的xml 文件,对于其他场景可能更多。这似乎很多工作。有没有更好的测试方法?

我应该使用最小起订量或其他测试框架来简化测试吗?

【问题讨论】:

    标签: unit-testing moq mstest moq-3


    【解决方案1】:

    使用起订量

    using System;
    using System.Xml;
    using Moq;
    using NUnit.Framework;
    
    namespace MockXmlTest
    {
        [TestFixture]
        public class MyServiceTests
        {
            private MockSetup _mockSetup;
            [SetUp]
            public void Init()
            {
                _mockSetup = MockSetup.HappySetup();
            }
            [Test]
            public void MyService_Should_Return_Guid()
            {
                //Arrange
                var myService = _mockSetup.MyService.Object;
                var id = 42;
                var expected = Guid.Empty.ToString();
    
                //Act
                var actual = myService.GetXml(id);
    
                //Assert
                Assert.AreEqual(expected, actual.FirstChild.InnerText);
            }
        }
    
        public class MyService : IMyService
        {
            public XmlDocument GetXml(int id)
            {
                var doc = new XmlDocument();
                //Do real stuff
                return doc;
            }
        }
    
        public interface IMyService
        {
            XmlDocument GetXml(int id);
        }
    
        public class MockSetup
        {
            public Mock<IMyService> MyService { get; set; }
    
            public MockSetup()
            {
                MyService = new Mock<IMyService>();
            }
            public static MockSetup HappySetup()
            {
                var mockSetup = new MockSetup();
    
                var mockDoc = CreateMockDoc();
    
                //Matches any id of an integer, returns a XmlDocument mock
                mockSetup.MyService.Setup(m => m.GetXml(It.IsAny<int>())).Returns(mockDoc);
                return mockSetup;
            }
    
            private static XmlDocument CreateMockDoc()
            {
                //<Main><MyGuid>00000000-0000-0000-0000-000000000000</MyGuid></Main>
                XmlDocument mockDoc = new XmlDocument();
                XmlElement el = (XmlElement)mockDoc.AppendChild(mockDoc.CreateElement("Main"));
    
                el.AppendChild(mockDoc.CreateElement("MyGuid")).InnerText = It.IsAny<Guid>().ToString();
                return mockDoc;
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以只创建要测试的元素,而无需读取文件:

      var doc = new XmlDocument();
      doc.LoadXml("<MyTestElement/>");
      var myTestElement = doc.DocumentElement;
      myTestElement.Attributes["employeeNo"] = "fakeId";
      
      var response = myTestResponder.GetData(myTestElement);
      
      //assert whatever you need to
      

      注意:每次你发现测试太难写时,通常这意味着你的类/方法做得太多。

      我会假设,您的方法会验证输入,而不是对提供的数据进行验证。我建议您抽象数据读取部分(使用一些 xml 反序列化器)来填充您的应用程序所需的数据模型。

      然后对反序列化数据的结果运行验证。比如:

      public MessageResponse GetData(XmlElement requestElement)
      {
         var data = _xmlDeserializer.Deserialize(requestElement);
         var validationResult = _validator.Validate(data);
          if (validationResult.Errors.Count > 0)
          {
               //populate errors
              return result;
          }
      
          _dataProcessor.DoSomethingWithData(data);
      }
      

      查看FluentValidation 以获得一个不错的验证库。

      如果你走上述路线,那么你的测试会简单得多。

      【讨论】:

      • 和你的想法一样,唯一的问题是实际的 xml 文件很小。并且需要验证很多字段。这将是一个长 xml 字符串。
      【解决方案3】:
      [TestMethod]
      public void GetData_Returns_Correct_Message_When_EmployeeNo_Is_Null()
      {
          var inputWithoutEmployeeNo = GetElement(@"<input></input>");
      
          var actual = GetData(inputWithoutEmployeeNo);
      
          Assert.IsTrue(actual.Error, "Error should be true when employee no. is missing");
          Assert.IsNotNull(actual.Messages);
          Assert.AreEqual(1, actual.Messages.Count);
          Assert.AreEqual("Attribute employeeNo is missing", actual.Messages[0]);
      }
      
      private XmlElement GetElement(string xml)
      {
          var doc = new XmlDocument();
          doc.LoadXml(xml);
          return doc.DocumentElement;
      }
      

      在进行单元测试时,我发现代码抛出了 NullReferenceException。 以下单元测试演示了该问题:

      [TestMethod]
      [ExpectedException(typeof(NullReferenceException))]
      public void GetData_Throws_NullReferenceException_When_EmployeeNo_Is_Not_Null_And_XmlEmployeeName_Is_Null()
      {
          var inputWithoutEmployeeNo = GetElement(@"<input employeeNo='123'></input>");
      
          GetData(inputWithoutEmployeeNo);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-02
        • 1970-01-01
        • 1970-01-01
        • 2016-09-20
        • 1970-01-01
        • 2022-10-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多