如何为多个代表分配/选择'moled'方法?

时间:2011-08-04 14:33:55

标签: moles

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

  1. 提供了为多个摩尔代表提供一个方法的能力,可能是在测试夹具设置级别吗?
  2. 在我的测试用例中切换运行时,我的鼹鼠代表必须调用哪一个即将被隔离的moled方法的调用?
  3. 任何提示?

1 个答案:

答案 0 :(得分:1)

最佳答案:

在绕行方法中包含门控逻辑比在同一方法中使用两个存根更容易并且更有意义!例如, 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();
}

TARGET TYPE:

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>文件是静态的,无法实例化:例如:

测试方法:

[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);
}

TARGET TYPE:

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();
    }
}

UPDATE:

在回应@Chai评论时,可以在测试项目中创建可以作为鼹鼠绕行代表引用的常用方法。例如,您可能希望编写可由任何单元测试引用的公共方法,该方法设置各种预配置方案。以下示例显示如何使用参数化方法。发挥创意 - 他们只是方法调用!

目标类型:

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()通过打开枚举参数来设置鼹鼠的弯路。这允许在许多测试中以干燥的方式建立相同的场景。

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.
        }
    }
}