Class 方法选择隐藏的基础 class 属性 而不是新的子 class 属性
Class method picks hidden base class property over new subclass property
我正在尝试为我的一个 classes 编写单元测试,它使用来自第三方库的基础 class,但我的第一次尝试很脆弱,因为测试取决于与内容管理员管理的文本集成。
首先,一个小的复制品。这将代表第三方基地 class 我必须与之合作:
namespace ThirdPartyXyz
{
public class SomeFancyBaseClass
{
public SomeFancyBaseClass()
{
this.myMap = new Dictionary<string, string> { { "title", "Greatness" } };
}
public IReadOnlyDictionary<string, string> myMap { get; private set; }
}
}
然后我按照这些行对代码进行单元测试:
namespace MyCorp
{
public class MyThing : ThirdPartyXyz.SomeFancyBaseClass
{
public string GetHeadline()
{
return "[" + this.myMap["title"] + "]";
}
}
}
使用以下 NUnit 测试方法:
namespace MyCorp.UnitTestExperiments
{
[TestFixture]
public class MyThingTests
{
[Test]
public void GetHeadlineWillOutputBracketedResource()
{
var thing = new MyThing();
var result = thing.GetHeadline();
Assert.That(result, Is.EqualTo("[Greatness]"));
}
}
}
这是绿色的,但如果内容管理员将 "Greatness"
值更改为其他值,它将变为红色。所以我试图从基础 class 模拟/存根/伪造实际字典,但这并不简单,因为第三方库将 myMap
的字典 setter 声明为 private
.
这是我试过的方法:
namespace MyCorp.UnitTestExperiments
{
[TestFixture]
public class MyThingTests
{
private class TestableMyThing : MyThing
{
public TestableMyThing(Dictionary<string, string> texts) { this.myMap = texts; }
public new IReadOnlyDictionary<string, string> myMap { get; private set; }
}
[Test]
public void GetHeadlineWillOutputBracketedResource()
{
var fakeTexts = new Dictionary<string, string> { { "title", "test text" } };
var thing = new TestableMyThing(fakeTexts);
var result = thing.GetHeadline();
Assert.That(result, Is.EqualTo("[test text]"));
}
}
}
但是,这不起作用:测试仍然失败。 GetHeadline
方法使用来自 SomeFancyBaseClass
的隐藏 myMap
属性 而不是包含 "test text"
.
的伪字典
我目前的目标/问题有两个。首先,到目前为止,我很好奇我 (c/w) 如何获得我当前的工作方法。但其次,我担心让 MyThing
可测试的方法不是最好的方法,我想知道是否有办法完全避免这种情况。
三个选项:
使用反射访问字典的set
er
第二个选项,让你的class更容易存根:
public class MyThing : ThirdPartyXyz.SomeFancyBaseClass
{
public virtual new IReadOnlyDictionary<string, string> myMap;
{
get { return base.myMap; }
}
public string GetHeadline()
{
// this will use your 'virtual new myMap'!
return "[" + this.myMap["title"] + "]";
}
}
现在在你的单元测试中你可以:
public class MyThingTestable : MyThing
{
public override IReadOnlyDictionary<string, string> myMap { get; set; }
}
现在你可以设置myMap
,你的class将使用它(但注意SomeFancyBaseClass
class不会使用它!它会用它myMap
!不是很好!)
第三种选择:使用 Microsoft Fakes 或类似产品。
我正在尝试为我的一个 classes 编写单元测试,它使用来自第三方库的基础 class,但我的第一次尝试很脆弱,因为测试取决于与内容管理员管理的文本集成。
首先,一个小的复制品。这将代表第三方基地 class 我必须与之合作:
namespace ThirdPartyXyz
{
public class SomeFancyBaseClass
{
public SomeFancyBaseClass()
{
this.myMap = new Dictionary<string, string> { { "title", "Greatness" } };
}
public IReadOnlyDictionary<string, string> myMap { get; private set; }
}
}
然后我按照这些行对代码进行单元测试:
namespace MyCorp
{
public class MyThing : ThirdPartyXyz.SomeFancyBaseClass
{
public string GetHeadline()
{
return "[" + this.myMap["title"] + "]";
}
}
}
使用以下 NUnit 测试方法:
namespace MyCorp.UnitTestExperiments
{
[TestFixture]
public class MyThingTests
{
[Test]
public void GetHeadlineWillOutputBracketedResource()
{
var thing = new MyThing();
var result = thing.GetHeadline();
Assert.That(result, Is.EqualTo("[Greatness]"));
}
}
}
这是绿色的,但如果内容管理员将 "Greatness"
值更改为其他值,它将变为红色。所以我试图从基础 class 模拟/存根/伪造实际字典,但这并不简单,因为第三方库将 myMap
的字典 setter 声明为 private
.
这是我试过的方法:
namespace MyCorp.UnitTestExperiments
{
[TestFixture]
public class MyThingTests
{
private class TestableMyThing : MyThing
{
public TestableMyThing(Dictionary<string, string> texts) { this.myMap = texts; }
public new IReadOnlyDictionary<string, string> myMap { get; private set; }
}
[Test]
public void GetHeadlineWillOutputBracketedResource()
{
var fakeTexts = new Dictionary<string, string> { { "title", "test text" } };
var thing = new TestableMyThing(fakeTexts);
var result = thing.GetHeadline();
Assert.That(result, Is.EqualTo("[test text]"));
}
}
}
但是,这不起作用:测试仍然失败。 GetHeadline
方法使用来自 SomeFancyBaseClass
的隐藏 myMap
属性 而不是包含 "test text"
.
我目前的目标/问题有两个。首先,到目前为止,我很好奇我 (c/w) 如何获得我当前的工作方法。但其次,我担心让 MyThing
可测试的方法不是最好的方法,我想知道是否有办法完全避免这种情况。
三个选项:
使用反射访问字典的
set
er第二个选项,让你的class更容易存根:
public class MyThing : ThirdPartyXyz.SomeFancyBaseClass { public virtual new IReadOnlyDictionary<string, string> myMap; { get { return base.myMap; } } public string GetHeadline() { // this will use your 'virtual new myMap'! return "[" + this.myMap["title"] + "]"; } }
现在在你的单元测试中你可以:
public class MyThingTestable : MyThing { public override IReadOnlyDictionary<string, string> myMap { get; set; } }
现在你可以设置
myMap
,你的class将使用它(但注意SomeFancyBaseClass
class不会使用它!它会用它myMap
!不是很好!)第三种选择:使用 Microsoft Fakes 或类似产品。