如何模拟 IEnumerable<interface> 类型并将其传递给构造函数
How to mock an IEnumerable<interface> type and pass it to constructor
我得到了一个 class,如下所示
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:上面测试class中的行_myClass = new MyClass(_locationProvider.Object)
,因为构造函数的形式参数是ILocationProvider[]
所以我不能通过一个Mock<IEnumerable<ILocationProvider>>
的模拟对象
问题 2: 如果我将上面 MyClass
中的行 private readonly IEnumerable<ILocationProvider> _locationProvider;
更改为 private readonly ILocationProvider[] _locationProvider;
我将无法将其模拟为因为 mock 必须是接口或抽象或非密封 class.
问题3:如何在我的测试方法
中设置_locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type));
问题 4: 如何断言我的方法 ProvideRequireLocationObject
正在返回 ILocationProvider
的类型
我相信是从错误的角度来看它。我认为你不需要模拟 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
.....
}
}
首先,你不需要模拟集合。集合(数组或列表)经过充分测试以信任它们的实现。由于您的构造函数需要一个数组,因此您需要 传递一个数组。最简单的方法就是简单地传递一个数组。根本没有理由嘲笑这个。
更改您正在测试的 class 的 实现细节 (如问题 2 中所建议的)不会改变测试表面上的任何内容。无论如何,单元测试应该始终独立于内部实现细节。
How do I assert that my method ProvideRequireLocationObject
is returning a type of ILocationProvider
你不需要那样做。该方法具有 return 类型,因此编译器将只接受方法 return 类型的实现。语言保证 如果有 return 值 ,那么它是 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; }
}
}
当然,您可以根据需要扩展这些测试。
我得到了一个 class,如下所示
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:上面测试class中的行_myClass = new MyClass(_locationProvider.Object)
,因为构造函数的形式参数是ILocationProvider[]
所以我不能通过一个Mock<IEnumerable<ILocationProvider>>
问题 2: 如果我将上面 MyClass
中的行 private readonly IEnumerable<ILocationProvider> _locationProvider;
更改为 private readonly ILocationProvider[] _locationProvider;
我将无法将其模拟为因为 mock 必须是接口或抽象或非密封 class.
问题3:如何在我的测试方法
中设置_locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type));
问题 4: 如何断言我的方法 ProvideRequireLocationObject
正在返回 ILocationProvider
我相信是从错误的角度来看它。我认为你不需要模拟 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
.....
}
}
首先,你不需要模拟集合。集合(数组或列表)经过充分测试以信任它们的实现。由于您的构造函数需要一个数组,因此您需要 传递一个数组。最简单的方法就是简单地传递一个数组。根本没有理由嘲笑这个。
更改您正在测试的 class 的 实现细节 (如问题 2 中所建议的)不会改变测试表面上的任何内容。无论如何,单元测试应该始终独立于内部实现细节。
How do I assert that my method
ProvideRequireLocationObject
is returning a type ofILocationProvider
你不需要那样做。该方法具有 return 类型,因此编译器将只接受方法 return 类型的实现。语言保证 如果有 return 值 ,那么它是 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; }
}
}
当然,您可以根据需要扩展这些测试。