2

在等待我的 VS 2010 许可证时,我目前正在从外部检查 Moles,我想知道 Moles 是否允许我:

  1. 提供为一个被mole的方法分配多个mole代表的能力,也许是在测试夹具设置级别?
  2. 在我的测试用例中在运行时切换,对于即将被隔离的 moled 方法的调用,必须调用我的哪些 moole 代表?

有什么提示吗?

4

1 回答 1

1

最佳答案:

在 detour 方法中包含门控逻辑比在同一方法中使用两个存根要容易得多,也更有意义!例如,MyMethod从磁盘上的三个不同文件中读取数据,每个文件都需要返回不同的模拟数据。我们可以绕过System.IO.File.OpenRead并通过分析OpenRead的输入参数来门控返回值:

测试方法:

[TestMethod]
[HostType("Moles")]
public void Test()
{
    System.IO.Moles.MFile.OpenReadString = filePath => {
        var mockStream = new System.IO.FileStream();
        byte[] buffer;
        switch (filePath)
        {
            case @"C:\DataFile.dat":
                mockStream.Write(buffer, 0, 0); // Populate stream
                break;
            case @"C:\TextFile.txt":
                mockStream.Write(buffer, 0, 0); // Populate stream
                break;
            case @"C:\LogFile.log":
                mockStream.Write(buffer, 0, 0); // Populate stream
                break;
        }
        return mockStream;
    };


    var target = new MyClass();
    target.MyMethod();
}

目标类型:

using System.IO;
public class MyClass
{
    public void MyMethod()
    {
        var fileAData = File.OpenRead(@"C:\DataFile.dat");
        var fileBData = File.OpenRead(@"C:\TextFile.txt");
        var fileCData = File.OpenRead(@"C:\LogFile.log");
    }
}

直接回答您的问题:

是 #1:为每个绕行实例化一种类型,然后将每种类型用于所需的行为。并且,对#2 是的:对鼹鼠类型的一个实例或另一个实例采取行动。这需要添加方法输入参数或类构造函数注入。

例如,MyMethod从磁盘读取三个数据文件,您需要传回三个不同的数据模拟。 MyMethod需要三个参数,这是一个明显侵入性的解决方案。(注意输入参数为 FileInfo 类型;因为 System.IO>File 是静态的,不能实例化:例如:

测试方法:

[TestMethod]
[HostType("Moles")]
public void Test()
{
    var fileInfoMoleA = new System.IO.Moles.MFileInfo();
    fileInfoMoleA.OpenRead = () => { return new FileStream(); };

    var fileInfoMoleB = new System.IO.Moles.MFileInfo();
    fileInfoMoleB.OpenRead = () => { return new FileStream(); };

    var fileInfoMoleC = new System.IO.Moles.MFileInfo();
    fileInfoMoleC.OpenRead = () => { return new FileStream(); };

    var target = new MyClass();
    target.MyMethod(fileInfoMoleA, fileInfoMoleB, fileInfoMoleC);
}

目标类型:

using System.IO;
public class MyClass
{
    // Input parameters are FileInfo type; because, System.IO.File
    // is a static class, and can not be instantiated.
    public void MyMethod(FileInfo fileInfoA, FileInfo fileInfoB, FileInfo fileInfoC)
    {
        var fileAData = fileInfoA.OpenRead();
        var fileBData = fileInfoB.OpenRead();
        var fileCData = fileInfoC.OpenRead();
    }
}

更新:

作为对@Chai 评论的回应,可以在测试项目中创建通用方法,这些方法可以被引用为mole detour 委托。例如,您可能希望编写一个可以被任何单元测试引用的通用方法,它设置了各种预配置的场景。以下示例显示了如何使用参数化方法。发挥创意——它们只是方法调用!

目标类型:

namespace PexMoleDemo
{
    public class MyClass
    {
        private MyMath _math;
        public MyClass()
        {
            _math = new MyMath() { left = 1m, right = 2m };
        }

        public decimal GetResults()
        {
            return _math.Divide();
        }
    }

    public class MyOtherClass
    {
        private MyMath _math;
        public MyOtherClass()
        {
            _math = new MyMath() { left = 100m, right = 200m };
        }

        public decimal Divide()
        {
            return _math.Divide();
        }
    }

    public class MyMath
    {
        public decimal left { get; set; }
        public decimal right { get; set; }

        public decimal Divide()
        {
            return left / right;
        }
    }
}

测试方法: ArrangeScenarios() 通过打开枚举参数设置摩尔绕道。这允许在许多测试中以 DRY 方式建立相同的场景。

using System;
using Microsoft.Moles.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PexMoleDemo;
[assembly: MoledAssembly("PexMoleDemo")]

namespace TestProject1
{
    [TestClass()]
    public class ProgramTest
    {
        public enum Scenarios
        {
            DivideByZero,
            MultiplyInsteadOfDivide
        }

        private void ArrangeScenario(Scenarios scenario)
        {
            switch (scenario)
            {
                case Scenarios.DivideByZero:
                    PexMoleDemo.Moles.MMyMath.AllInstances.rightGet = 
                        instance => { return 0m; };
                    break;
                case Scenarios.MultiplyInsteadOfDivide:
                    PexMoleDemo.Moles.MMyMath.AllInstances.Divide = 
                        instance => { return instance.left * instance.right; };
                    break;
                default:
                    throw new NotImplementedException("Invalid scenario.");
            }
        }

        [TestMethod]
        [HostType("Moles")]
        [ExpectedException(typeof(DivideByZeroException))]
        public void Test1()
        {
            ArrangeScenario(Scenarios.DivideByZero);
            var target = new PexMoleDemo.MyClass();

            var math = new PexMoleDemo.MyMath() { left = 1, right = 2 };
            var left = math.left;
            var right = math.right;


            var actual = target.GetResults();
        }

        [TestMethod]
        [HostType("Moles")]
        public void Test2()
        {
            ArrangeScenario(Scenarios.MultiplyInsteadOfDivide);
            // Perform some sort of test that determines if code breaks
            // when values are multiplied instead of divided.
        }

        [TestMethod]
        [HostType("Moles")]
        [ExpectedException(typeof(DivideByZeroException))]
        public void Test3()
        {
            ArrangeScenario(Scenarios.DivideByZero);
            var target = new PexMoleDemo.MyOtherClass();

            var math = new PexMoleDemo.MyMath() { left = 1, right = 2 };
            var left = math.left;
            var right = math.right;


            var actual = target.Divide();
        }

        [TestMethod]
        [HostType("Moles")]
        public void Test4()
        {
            ArrangeScenario(Scenarios.MultiplyInsteadOfDivide);
            // Perform some sort of test that determines if code breaks
            // when values are multiplied instead of divided.
        }
    }
}
于 2011-08-04T23:47:37.740 回答