【问题标题】:Making multiple calls in unit test without using a for loop在不使用 for 循环的情况下在单元测试中进行多次调用
【发布时间】:2012-02-22 18:43:28
【问题描述】:

假设我有一个类似于下面的单元测试,有没有办法编写一个单元测试而不是多个单元测试,但也可以避免在单元测试中使用 for 循环?

[Test]
public void RunTestWithMultipleOptions()
{
  MyClass code = new MyClass();
  code.Prefix = "{DS1}";  //Options are {DS1}, {DS2}, {DS3}, {DS4}
  //Property could be set to
  //code.Prefix = "{DS1}{DS2}";
  //code.Prefix = "{DS1}{DS2}{DS3}";

  //And so on

 //Based on how many {DS} used a method needs calling

  code.InputDataStore(1,"Data1");

  //If used {DS1}{DS2} in Prefix then
  //code.InputDataStore(1,"Data1");
  //code.InputDataStore(2,"Data2");

  //If used {DS1}{DS2}{DS3} in Prefix then
  //code.InputDataStore(1,"Data1");
  //code.InputDataStore(2,"Data2");
  //code.InputDataStore(3,"Data3");

  string OutputData = String.Empty;

  code.Output += delegate(int Id, string Data) 
                 { 
                    if (Id == (int)OutputsEnum.OutputModified)
                      OutputData = Data; 
                 };



  //Call the input method which will raise the Output event which we can assert against
  code.Input("hi there");

  //Assert that output has replace the prefix {DS} with the data in the datastorecontent list

  Assert.AreEqual("Data1hi there", OutputData);
}

我可以将属性值传递给单元测试方法并使用测试用例,但基于属性是什么 MyMethod 需要调用 x 次。如果没有在测试中添加循环,我想不出没有将所有渗透作为单独的单元测试的方法。

更新:以下是课程的主要内容:

    public event Action<int, string> Output;

    public string Prefix { get; set; }

    public string Postfix { get; set; }

    private List<string> DataStoreContents = new List<string>() { "", "", "", "" };

    public void Input(string Data)
    {
        if (Output != null)
            {
                if (!String.IsNullOrEmpty(Prefix))
                {
                    Prefix = Prefix.Replace("{DS1}", DataStoreContents[0]);
                    Prefix = Prefix.Replace("{DS2}", DataStoreContents[1]);
                    Prefix = Prefix.Replace("{DS3}", DataStoreContents[2]);
                    Prefix = Prefix.Replace("{DS4}", DataStoreContents[3]);
                }

                if (!String.IsNullOrEmpty(Postfix))
                {
                    Postfix = Postfix.Replace("{DS1}", DataStoreContents[0]);
                    Postfix = Postfix.Replace("{DS2}", DataStoreContents[1]);
                    Postfix = Postfix.Replace("{DS3}", DataStoreContents[2]);
                    Postfix = Postfix.Replace("{DS4}", DataStoreContents[3]);
                }

                Output((int)OutputsEnum.OutputBeforeModified, Data);
                Output((int)OutputsEnum.OutputModified, Prefix + Data + Postfix);
                Output((int)OutputsEnum.OutputAfterModified, Data);
            }
        }
    } 

    public void InputDataStore(int DataStore, string Data)
    {
        if (DataStore < 1 || DataStore > 4)
            throw new ArgumentOutOfRangeException("Datastore number out of range");

        DataStoreContents[DataStore - 1] = Data;
    }

}

我想测试一下,当我调用 InputDataStore(1,"MyData1"); InputDataStore(2, "MyData"); 时,Output 实际上确实将相关的 {DS1} 值替换为相关的字符串,并将其与任何其他 {DS} 值结合起来

【问题讨论】:

  • 如果需要,为什么不能使用循环?
  • 如果你在测试你的方法在多次执行时的行为(比如在for循环中),那么在我看来,循环是完全合适的。
  • 为什么不在测试中放置 for 循环是不好的做法?就像“ 被认为是有害的”的任何信念一样,事物的使用总是完全合理的,试图避免它只会创建不太可维护的代码。你想要一个循环,所以使用一个循环。 :-)
  • 你测试的方法好像很臭
  • @Jon:测试中的循环在偷工减料或重复测试代码逻辑方面很糟糕。当你需要使用循环时,把它当作早先出现问题的标志(但有时它是唯一的方法)。为什么你的 string 属性表现得好像它是一个列表?它几乎是在问“让我成为一个列表/集合”

标签: c# .net unit-testing nunit


【解决方案1】:

一种选择是将每个调用分开测试,然后将它们全部一起测试。如果您测试 A&B&C,您可以将自己限制为单独测试 A、B、C 和同时测试 A&B&C。一个选项是下一个代码(做了一些假设):

 [TestFixture]
    public class ToTestFixture
    {
        [SetUp]
        public void SetUp()
        {
            _instance = new ToTest();
            _instance.InputDataStore(1, "1");
            _instance.InputDataStore(2, "2");
            _instance.InputDataStore(3, "3");
            _instance.InputDataStore(4, "4");
        }

        private ToTest _instance;
        [TestCase("{DS1}","1")]
        [TestCase("{DS2}", "2")]
        [TestCase("{DS3}", "3")]
        [TestCase("{DS4}", "4")]
        [TestCase("{DS1}{DS2}{DS3}{DS4}", "1234")]
        [Test]
        public void TestPrefixReplacements(string input, string expectedResult)
        {

            _instance.Prefix = input;

            //Call the input method which will raise the Output event which we can test
            _instance.Input("Any string goes here as we test only prefix." );

            Assert.AreEqual(expectedResult, _instance.Prefix);
        }

    }

    internal enum OutputsEnum
    {
        OutputBeforeModified,
        OutputModified,
        OutputAfterModified
    }

    public class ToTest
    {
        public event Action<int, string> Output = (x, result) => Console.WriteLine(x.ToString() + result);

        public string Prefix { get; set; }

        public string Postfix { get; set; }

        private List<string> DataStoreContents = new List<string>() {"1", "2", "3", "4"};

        public void Input(string Data)
        {
            if (Output != null)
            {
                if (!String.IsNullOrEmpty(Prefix))
                {
                    Prefix = Prefix.Replace("{DS1}", DataStoreContents[0]);
                    Prefix = Prefix.Replace("{DS2}", DataStoreContents[1]);
                    Prefix = Prefix.Replace("{DS3}", DataStoreContents[2]);
                    Prefix = Prefix.Replace("{DS4}", DataStoreContents[3]);
                }

                if (!String.IsNullOrEmpty(Postfix))
                {
                    Postfix = Postfix.Replace("{DS1}", DataStoreContents[0]);
                    Postfix = Postfix.Replace("{DS2}", DataStoreContents[1]);
                    Postfix = Postfix.Replace("{DS3}", DataStoreContents[2]);
                    Postfix = Postfix.Replace("{DS4}", DataStoreContents[3]);
                }

                Output((int) OutputsEnum.OutputBeforeModified, Data);
                Output((int) OutputsEnum.OutputModified, Prefix + Data + Postfix);
                Output((int) OutputsEnum.OutputAfterModified, Data);
            }
        }
        public void InputDataStore(int DataStore, string Data)
        {
            if (DataStore < 1 || DataStore > 4)
                throw new ArgumentOutOfRangeException("Datastore number out of range");

            DataStoreContents[DataStore - 1] = Data;
        }
    }

无论如何,我觉得“DS1”和数组索引之间存在联系。 (1-0, 2-1)。这意味着下一次重构是可能的:

Prefix = Prefix.Replace("{DS"+index+"}", DataStoreContents[index-1]);

除此之外,我猜输出动作决策很奇怪,两个 if-s 是重复的代码。这就是我的意思:

   public void Input(string Data)
    {
        if (Output != null)
        {
            Prefix = ApplyReplaceRules(Prefix);

            Postfix = ApplyReplaceRules(Postfix);

            Output((int) OutputsEnum.OutputBeforeModified, Data);
            Output((int) OutputsEnum.OutputModified, Prefix + Data + Postfix);
            Output((int) OutputsEnum.OutputAfterModified, Data);
        }
    }

    private string ApplyReplaceRules(string patternString)
    {
        if (!String.IsNullOrEmpty(Postfix))
        {
            patternString = patternString.Replace("{DS1}", DataStoreContents[0]);
            patternString = patternString.Replace("{DS2}", DataStoreContents[1]);
            patternString = patternString.Replace("{DS3}", DataStoreContents[2]);
            patternString = patternString.Replace("{DS4}", DataStoreContents[3]);
        }

        return patternString;
    }

【讨论】:

    猜你喜欢
    • 2015-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    • 1970-01-01
    相关资源
    最近更新 更多