如何模拟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
首先,您不需要模拟集合。集合(数组或列表)经过充分测试,可以信任它们的实现。由于构造函数需要一个数组,因此需要传递一个数组。最简单的方法就是简单地传递一个数组。根本没有理由嘲笑这一点。
更改正在测试的类的实现细节(如问题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; }
}
}
当然,您可以根据需要扩展这些测试。
我认为他们从错误的角度看待它。我认为你不需要嘲笑IEnumerable
(Mock<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
.....
}
}