C# 模拟具体 class。如何?
C# Mock concrete class. How?
我想模拟一个具体的 class,具体来说是 SortedDictionary。
上下文:
我有一个 LocationMapper class 定义如下:
public class LocationMapper
{
private SortedDictionary<string, Location>() locationMap;
public LocationMapper()
{
this.locationMap = new SortedDictionary<string, Location>();
}
public LocationMapper(SortedDictionary<string, Location> locations)
{
this.locationMap = locations;
}
public Location AddLocation(Location location)
{
if(! locationMap.ContainsKey(location.Name))
{
locationMap.Add(location.Name, location)
}
return locationMap[location.Name];
}
}
为了单元测试 AddLocation(),我需要模拟具体的 class SortedDictionary<>。不幸的是,NSubstitute 不允许这样做。
The unit test that I had envisioned to write is below
[Test]
public void AddLocation_ShouldNotAddLocationAgainWhenAlreadyPresent()
{
var mockLocationMap = ;//TODO
//Stub mockLocationMap.ContainsKey(Any<String>) to return "true"
locationMapper = new LocationMapper(mockLocationMap);
locationMapper.AddLocation(new Location("a"));
//Verify that mockLocationMap.Add(..) is not called
}
您将如何在 DotNet 中编写这种风格的单元测试?还是因为已知的约束你不走这条路?
非常感谢您的帮助。
你不应该在这里模拟字典。实际上它是 LocationMapper
class 的一个实现细节。它应该通过封装隐藏起来。您可以使用其他任何东西来存储位置——数组、列表或简单的字典。 LocationMapper
是否满足其要求并不重要。这种情况下有什么要求?像
Location mapper should be able to map location which was added to mapper
目前您的映射器非常无用,它不会为字典行为添加任何内容。您缺少核心 - 映射。我只能假设这个 class 将如何使用。您需要一些 public 接口来进行映射。测试应该如下所示(此处使用 AutoFixture 和 FluentAssertions):
var mapper = new LocationMapper();
var location = fixture.Create<Location>();
mapper.AddLocation(location);
mapper.Map(location.Name).Should().Be(location);
当此测试通过时,您可以将位置添加到映射器并使用映射器映射这些位置。
您有两个选择:如果您使用 VS Enterprise,请使用 Microsoft Fakes 为您的 class 生成 Shim。 (如果你想要样品,请联系我)>
如果您不使用 VS Enterprise(作为这里的大多数人),您将不得不求助于反思:
[Test]
public void AddLocation_ShouldNotAddLocationAgainWhenAlreadyPresent()
{
var locationMapper = new LocationMapper(mockLocationMap);
locationMapper.AddLocation(new Location("a"));
var dict = ((SortedDictionary<string, Location>)typeof(LocationMapper).GetField("locationMap", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(locationMapper));
Assert.AreEqual("a", dict.FirstOrDefault().Name)
}
另一种方法是使用单元测试工具,它允许您模拟具体 类,例如我正在使用 Typemock Isolator 并且能够创建您想要进行的测试:
[TestMethod]
public void TestMethod1()
{
var fakeLocationMap = Isolate.Fake.Instance<SortedDictionary<string, Location>>();
Isolate.WhenCalled(() => fakeLocationMap.ContainsKey(string.Empty)).WillReturn(true);
var instance = new LocationMapper(fakeLocationMap);
var res = instance.AddLocation(new Location("a"));
Isolate.Verify.WasNotCalled(() => fakeLocationMap.Add(string.Empty, null));
}
我想模拟一个具体的 class,具体来说是 SortedDictionary。
上下文:
我有一个 LocationMapper class 定义如下:
public class LocationMapper
{
private SortedDictionary<string, Location>() locationMap;
public LocationMapper()
{
this.locationMap = new SortedDictionary<string, Location>();
}
public LocationMapper(SortedDictionary<string, Location> locations)
{
this.locationMap = locations;
}
public Location AddLocation(Location location)
{
if(! locationMap.ContainsKey(location.Name))
{
locationMap.Add(location.Name, location)
}
return locationMap[location.Name];
}
}
为了单元测试 AddLocation(),我需要模拟具体的 class SortedDictionary<>。不幸的是,NSubstitute 不允许这样做。
The unit test that I had envisioned to write is below
[Test]
public void AddLocation_ShouldNotAddLocationAgainWhenAlreadyPresent()
{
var mockLocationMap = ;//TODO
//Stub mockLocationMap.ContainsKey(Any<String>) to return "true"
locationMapper = new LocationMapper(mockLocationMap);
locationMapper.AddLocation(new Location("a"));
//Verify that mockLocationMap.Add(..) is not called
}
您将如何在 DotNet 中编写这种风格的单元测试?还是因为已知的约束你不走这条路?
非常感谢您的帮助。
你不应该在这里模拟字典。实际上它是 LocationMapper
class 的一个实现细节。它应该通过封装隐藏起来。您可以使用其他任何东西来存储位置——数组、列表或简单的字典。 LocationMapper
是否满足其要求并不重要。这种情况下有什么要求?像
Location mapper should be able to map location which was added to mapper
目前您的映射器非常无用,它不会为字典行为添加任何内容。您缺少核心 - 映射。我只能假设这个 class 将如何使用。您需要一些 public 接口来进行映射。测试应该如下所示(此处使用 AutoFixture 和 FluentAssertions):
var mapper = new LocationMapper();
var location = fixture.Create<Location>();
mapper.AddLocation(location);
mapper.Map(location.Name).Should().Be(location);
当此测试通过时,您可以将位置添加到映射器并使用映射器映射这些位置。
您有两个选择:如果您使用 VS Enterprise,请使用 Microsoft Fakes 为您的 class 生成 Shim。 (如果你想要样品,请联系我)>
如果您不使用 VS Enterprise(作为这里的大多数人),您将不得不求助于反思:
[Test]
public void AddLocation_ShouldNotAddLocationAgainWhenAlreadyPresent()
{
var locationMapper = new LocationMapper(mockLocationMap);
locationMapper.AddLocation(new Location("a"));
var dict = ((SortedDictionary<string, Location>)typeof(LocationMapper).GetField("locationMap", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(locationMapper));
Assert.AreEqual("a", dict.FirstOrDefault().Name)
}
另一种方法是使用单元测试工具,它允许您模拟具体 类,例如我正在使用 Typemock Isolator 并且能够创建您想要进行的测试:
[TestMethod]
public void TestMethod1()
{
var fakeLocationMap = Isolate.Fake.Instance<SortedDictionary<string, Location>>();
Isolate.WhenCalled(() => fakeLocationMap.ContainsKey(string.Empty)).WillReturn(true);
var instance = new LocationMapper(fakeLocationMap);
var res = instance.AddLocation(new Location("a"));
Isolate.Verify.WasNotCalled(() => fakeLocationMap.Add(string.Empty, null));
}