C#模拟具体类。怎么样?

时间:2017-03-08 20:38:20

标签: c# unit-testing mocking tdd

我想模拟一个具体的类,具体是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];
  }  
}

要单元测试AddLocation(),我需要模拟具体类SortedDictionary&lt;&gt;。不幸的是,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中以这种方式编写单元测试?或者你没有采取这条路径来解决已知的约束?

非常感谢您的帮助。

3 个答案:

答案 0 :(得分:3)

你不应该在这里模拟字典。实际上它是LocationMapper类的实现细节。它应该通过封装来隐藏。您可以使用其他任何东西来存储位置 - 数组,列表或简单字典。 LocationMapper是否符合其要求并不重要。在这种情况下有什么要求?像

这样的东西
  

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

目前你的映射器非常无用,它对字典行为没有任何补充。您错过了核心 - 映射。我只能假设这个课程将如何使用。您需要一些公共接口进行映射。测试应该看起来像(此处使用的AutoFixture和FluentAssertions):

var mapper = new LocationMapper();
var location = fixture.Create<Location>();
mapper.AddLocation(location);
mapper.Map(location.Name).Should().Be(location);

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

答案 1 :(得分:3)

另一种方法是使用单元测试工具,允许您模拟具体类,例如我使用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));
}

答案 2 :(得分:0)

您有两种选择:如果您使用VS Enterprise,请使用Microsoft Fakes为您的班级生成Shim。 (如果你想要样品,请打电话)&gt;

如果你不使用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)
}