如何模拟IEnumerable<;界面>;键入并将其传递给构造函数

本文关键字:构造函数 界面 何模拟 模拟 lt IEnumerable gt | 更新日期: 2023-09-27 17:59:04

我得到了一个类,它看起来像下面的

public interface ILocationProvider
{
    bool IsRequiredLocation (string type);
}
public class MyClass : IMyInterface
{
    private readonly IEnumerable<ILocationProvider> _locationProvider;
    public MyClass (ILocationProvider[] locationProvider)
    {
        _locationProvider = locationProvider;
    }
    public ILocationProvider ProvideRequireLocationObject(string type)
    {
        ILocationProvider location = _locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type));
        return location;
    }
}

现在我正试图为它编写一些测试。但我坚持将Mock<IEnumerable<ITransitReportCountryFlowProvider>>传递给构造函数。低于我的测试代码

[TestClass]
public class TMyClassTest
{
    private Mock<IEnumerable<ILocationProvider>> _locationProvider = null;
    private IMyInterface _myClass = null;
    [TestInitialize]
    public void InitializeTest ()
    {
        _locationProvider = new Mock<IEnumerable<ILocationProvider>>();
    }
    [TestMethod]
    public void ProvideRequireLocationObject_Test1()
    {
        //Given: I have type as 'PMI'
        string type = "PMI";
        //When: I call MyClass object
        _myClass = new MyClass(_locationProvider.Object); //wrong actual argument as the formal argument is an array of ILocationProvider
        //_locationProvider.Setup(x => x.IsRequiredCountryFlow(It.IsAny<string>())).Returns(true); //how do I setup
        ILocationProvider result = _myClass.ProvideRequireLocationObject(type);
        //Then: I get a type of ILocationProvider in return
        Assert.IsTrue(result is ILocationProvider);
    }
}

问题1:上面测试类中的行_myClass = new MyClass(_locationProvider.Object),因为构造函数的形式参数是ILocationProvider[],所以我不能传递Mock<IEnumerable<ILocationProvider>> 的模拟对象

问题2:如果我将上面MyClass中的private readonly IEnumerable<ILocationProvider> _locationProvider;行更改为private readonly ILocationProvider[] _locationProvider;,我将无法对其进行mock,因为mock必须是接口、抽象或非密封类。

问题3:如何在测试方法中设置_locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type));

问题4:如何断言我的方法ProvideRequireLocationObject正在返回一种类型的ILocationProvider

如何模拟IEnumerable<;界面>;键入并将其传递给构造函数

首先,您不需要模拟集合。集合(数组或列表)经过充分测试,可以信任它们的实现。由于构造函数需要一个数组,因此需要传递一个数组。最简单的方法就是简单地传递一个数组。根本没有理由嘲笑这一点。

更改正在测试的类的实现细节(如问题2中所建议的)不会更改测试表面上的任何内容。无论如何,单元测试应该始终独立于内部实现细节。

如何断言我的方法ProvideRequireLocationObject正在返回一种类型的ILocationProvider

你不需要那样做。该方法具有该返回类型,因此编译器只接受该方法返回该类型的实现。语言保证如果有返回值,那么它是ILocationProvider类型的。所以你实际上只需要检查一下null

以您的实现为例,下面是测试这一点的一种可能方法。请注意,您实际上并不需要嘲笑这一点。当实际实现太难设置时(例如有其他依赖项),或者当提供可测试的实现工作量太大时(例如,一个有很多方法但只需要一个方法的接口),你通常会嘲笑一些事情。在这种情况下,我假设ILocationProvider很容易实现,所以我们将为此创建一个测试类型:

[TestClass]
public class MyClassTests
{
    [TestMethod]
    public void ProvideRequireLocationObject_EmptyCollection()
    {
        // arrange
        var providers = new ILocationProvider[] {};
        var obj = new MyClass(providers);
        // act
        var result = obj.ProvideRequireLocationObject();
        // assert
        Assert.IsNull(result);
    }
    [TestMethod]
    public void ProvideRequireLocationObject_NoRequiredLocation()
    {
        // arrange
        var providers = new ILocationProvider[] {
            new TestLocationProvider(false)
        };
        var obj = new MyClass(providers);
        // act
        var result = obj.ProvideRequireLocationObject();
        // assert
        Assert.IsNull(result);
    }
    [TestMethod]
    public void ProvideRequireLocationObject_OneRequiredLocation()
    {
        // arrange
        var providers = new ILocationProvider[] {
            new TestLocationProvider(true)
        };
        var obj = new MyClass(providers);
        // act
        var result = obj.ProvideRequireLocationObject();
        // assert
        Assert.AreEqual(providers[0], result);
    }
    [TestMethod]
    public void ProvideRequireLocationObject_OneRequiredLocationNotFirstInArray()
    {
        // arrange
        var providers = new ILocationProvider[] {
            new TestLocationProvider(false),
            new TestLocationProvider(true),
            new TestLocationProvider(false)
        };
        var obj = new MyClass(providers);
        // act
        var result = obj.ProvideRequireLocationObject();
        // assert
        Assert.AreEqual(providers[1], result);
    }
    [TestMethod]
    public void ProvideRequireLocationObject_MultipleRequiredLocations()
    {
        // arrange
        var providers = new ILocationProvider[] {
            new TestLocationProvider(true),
            new TestLocationProvider(true),
            new TestLocationProvider(true)
        };
        var obj = new MyClass(providers);
        // act
        var result = obj.ProvideRequireLocationObject();
        // assert
        Assert.AreEqual(providers[0], result);
    }

    public class TestLocationProvider : ILocationProvider
    {
        public TestLocationProvider(bool isRequiredLocation)
        {
            IsRequiredLocation = isRequiredLocation;
        }
        public bool IsRequiredLocation { get; private set; }
    }
}

当然,您可以根据需要扩展这些测试。

我认为他们从错误的角度看待它。我认为你不需要嘲笑IEnumerableMock<IEnumerable<ITransitReportCountryFlowProvider>>)-IEnumerable已经进行了前后测试,此外你也不想实现它的所有逻辑。。

我认为你应该嘲笑自己的课程:Mock<ITransitReportCountryFlowProvider>

并传递一个包含模拟的普通IEnumerable

类似于:

[TestClass]
public class TMyClassTest
{
    private Mock<ILocationProvider> _locationProvider = null;
    private IEnumerable<ILocationProvider> _locationProviderCollection;
    private IMyInterface _myClass = null;
    [TestInitialize]
    public void InitializeTest ()
    {
        _locationProvider = new Mock<IEnumerable<ILocationProvider>>();
        _locationProviderCollection = new List<ILocationProvider> { _locationProvider };
    }
    [TestMethod]
    public void ProvideRequireLocationObject_Test1()
    {
        //Given: I have type as 'PMI'
        string type = "PMI";
        //When: I call MyClass object
        _myClass = new MyClass(_locationProviderCollection); //wrong actual argument as the formal argument is an array of ILocationProvider
        .....
    }
}