C#模拟混凝土类。怎么用?

C#模拟混凝土类。怎么用?,c#,unit-testing,mocking,tdd,C#,Unit Testing,Mocking,Tdd,我想模拟一个具体的类,具体来说是SortedDictionary 上下文: 我有一个LocationMapper类,定义如下: public class LocationMapper { private SortedDictionary<string, Location>() locationMap; public LocationMapper() { this.locationMap = new SortedDictionary<string, Locat

我想模拟一个具体的类,具体来说是SortedDictionary

上下文:

我有一个LocationMapper类,定义如下:

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];
  }  
}
公共类位置映射器
{
私有SortedDictionary()locationMap;
公共位置映射器()
{
this.locationMap=new SortedDictionary();
}
公共位置映射器(分类词典位置)
{
this.locationMap=位置;
}
公共位置AddLocation(位置)
{
如果(!locationMap.ContainsKey(location.Name))
{
locationMap.Add(location.Name,location)
}
返回locationMap[location.Name];
}  
}
为了单元测试AddLocation(),我需要模拟具体的类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
}
下面是我设想编写的单元测试
[测试]
public void AddLocation_不应在HenalReadyPresent()中添加位置
{
var mockLocationMap=;//TODO
//存根mockLocationMap.ContainsKey(任意)返回“true”
locationMapper=新的locationMapper(mockLocationMap);
locationMapper.AddLocation(新位置(“a”));
//验证是否未调用mockLocationMap.Add(..)
}
您将如何在DotNet中以这种风格编写单元测试?或者,对于已知的约束,您不采用此路径?


非常感谢您的帮助。

您不应该在这里模仿字典。实际上,它是
LocationMapper
类的一个实现细节。它应该通过封装来隐藏。您可以使用其他任何东西来存储位置—数组、列表或简单字典。无论
LocationMapper
是否满足其要求。这种情况下的要求是什么?差不多

位置映射器应该能够映射添加到映射器中的位置

目前,你的映射器是非常无用的,它不会增加任何字典行为。你错过了核心映射。我只能假设这个类将如何使用。您需要一些用于映射的公共接口。测试应如下所示(此处使用的自动夹具和流体组件):

var mapper=newlocationmapper();
var location=fixture.Create();
mapper.AddLocation(位置);
mapper.Map(location.Name).Should().Be(location);

当此测试通过时,您可以向mapper添加位置,并使用mapper映射这些位置。

您不应该在此处模拟字典。实际上,它是
LocationMapper
类的一个实现细节。它应该通过封装来隐藏。您可以使用其他任何东西来存储位置—数组、列表或简单字典。无论
LocationMapper
是否满足其要求。这种情况下的要求是什么?差不多

位置映射器应该能够映射添加到映射器中的位置

目前,你的映射器是非常无用的,它不会增加任何字典行为。你错过了核心映射。我只能假设这个类将如何使用。您需要一些用于映射的公共接口。测试应如下所示(此处使用的自动夹具和流体组件):

var mapper=newlocationmapper();
var location=fixture.Create();
mapper.AddLocation(位置);
mapper.Map(location.Name).Should().Be(location);

当此测试通过时,您可以向mapper添加位置并使用mapper映射这些位置。

您有两个选项:如果您使用VS Enterprise,请使用Microsoft Fakes为您的类生成垫片。(如果您想要样品,请点击我)>

如果您不使用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)
}
[测试]
public void AddLocation_不应在HenalReadyPresent()中添加位置
{
var locationMapper=新的locationMapper(mockLocationMap);
locationMapper.AddLocation(新位置(“a”));
var dict=((SortedDictionary)typeof(LocationMapper).GetField(“locationMap”,BindingFlags.Instance | BindingFlags.NonPublic).GetValue(LocationMapper));
Assert.AreEqual(“a”,dict.FirstOrDefault().Name)
}

您有两种选择:如果您使用VS Enterprise,请使用Microsoft Fakes为您的类生成一个垫片。(如果您想要样品,请点击我)>

如果您不使用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)
}
[测试]
public void AddLocation_不应在HenalReadyPresent()中添加位置
{
var locationMapper=新的locationMapper(mockLocationMap);
locationMapper.AddLocation(新位置(“a”));
var dict=((SortedDictionary)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));
}
[TestMethod]
公共void TestMethod1()
{
var fakeLocationMap=Isolate.false.Instance();
Isolate.WhenCalled(()=>fakeLocationMap.ContainsKey(string.Empty)).WillReturn(true);
var实例=新位置映射器(fakeLocationMap);
var res=instance.AddLocation(新位置(“a”));
隔离.Verify.WasNotCalled(()=>fakeLocationMap.Add(string.Empty,null));
}

另一种方法是使用单元测试工具,该工具允许您模拟具体类,例如,我使用的是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));
}
[TestMethod]
公共void TestMethod1()
{
var fakeLocationMap=Isolate.false.Instance();
Isolate.WhenCalled(()=>fakeLocationMap.ContainsKey(string.Empty)).WillReturn(true);
var实例=新位置映射器(fakeLocationMap);
var res=instance.AddLocation(新位置(“a”));
隔离.Verify.WasNotCalled(()=>fakeLocationMap.Add(string.Empty,null));
}

为什么要嘲笑它?为什么不创建一个实例并传入呢?您可以完全控制如何填充它