当代码验证它收到的类型时,如何使用模拟
我想测试以下代码:
public IEnumerable<KeyValuePair<Fact, Exception>> ValidateAll()
{
//...do something
var invalidFacts = GetInvalidFacts();
//...do something
return duplicateFacts.Concat(invalidFacts);
}
private IEnumerable<KeyValuePair<Fact, Exception>> GetInvalidFacts()
{
var invalidFacts = Facts.Select(fact =>
{
try
{
fact.Validate();
return new KeyValuePair<Fact, Exception>(fact, null);
}
catch (FormatException e)
{
return new KeyValuePair<Fact, Exception>(fact, e);
}
catch (Exception e)
{
return new KeyValuePair<Fact, Exception>(fact, e);
}
}).Where(kv => kv.Value != null).ToList();
return invalidFacts;
}
基本上,测试的目标是验证“Facts”IEnumerable 中存在的所有对象是否都会调用其 Validate 方法。由于我对测试这些对象中的代码不感兴趣,已经有很多测试可以做到这一点,我想注入一个虚假事实列表。我正在使用最小起订量来制造假货。
所以我的单元测试看起来像这样:
[TestMethod]
public void ValidateAll_ValidateMethodIsInvokedOnAllFacts_WhenCalled()
{
var anyFactOne = new Mock<Fact>(); //Fact is an abstract class.
anyFactOne.Setup(f => f.Validate());
var dataWarehouseFacts = new DataWarehouseFacts { Facts = new Fact[] { anyFactOne.Object, FactGenerationHelper.GenerateRandomFact<SourceDetails>() } };
dataWarehouseFacts.ValidateAll();
}
现在我收到一个异常,因为代码实际上正在验证可以注入到 DataWarehouseFacts 类的事实类型,如下所示:
public IEnumerable<Fact> Facts
{
get
{
.....
}
set
{
var allowedTypes = new []
{
typeof(ProductUnitFact),
typeof(FailureFact),
typeof(DefectFact),
typeof(ProcessRunFact),
typeof(CustomerFact),
typeof(ProductUnitReturnFact),
typeof(ShipmentFact),
typeof(EventFact),
typeof(ComponentUnitFact),
typeof(SourceDetails)
};
if(!value.All(rootFact => allowedTypes.Contains(rootFact.GetType())))
throw new Exception ("DataWarehouseFacts can only be set with root facts");
ProductUnitFacts = value.OfType<ProductUnitFact>().ToList();
FailureFacts = value.OfType<FailureFact>().ToList();
DefectFacts = value.OfType<DefectFact>().ToList();
ProcessRunFacts = value.OfType<ProcessRunFact>().ToList();
CustomerFacts = value.OfType<CustomerFact>().ToList();
ProductUnitReturnFacts = value.OfType<ProductUnitReturnFact>().ToList();
ShipmentFacts = value.OfType<ShipmentFact>().ToList();
EventFacts = value.OfType<EventFact>().ToList();
ComponentUnitFacts = value.OfType<ComponentUnitFact>().ToList();
SourceDetails = value.OfType<SourceDetails>().Single();
}
}
绕过此验证的最佳方法是什么?
谢谢。
I want to test the following code:
public IEnumerable<KeyValuePair<Fact, Exception>> ValidateAll()
{
//...do something
var invalidFacts = GetInvalidFacts();
//...do something
return duplicateFacts.Concat(invalidFacts);
}
private IEnumerable<KeyValuePair<Fact, Exception>> GetInvalidFacts()
{
var invalidFacts = Facts.Select(fact =>
{
try
{
fact.Validate();
return new KeyValuePair<Fact, Exception>(fact, null);
}
catch (FormatException e)
{
return new KeyValuePair<Fact, Exception>(fact, e);
}
catch (Exception e)
{
return new KeyValuePair<Fact, Exception>(fact, e);
}
}).Where(kv => kv.Value != null).ToList();
return invalidFacts;
}
Basically the test's objective is to verify that all objects that exist within the "Facts" IEnumerable will call their Validate method. Since I'm not interested to test the code within those objects, there are already lots of tests that do that, I want to inject a list of fake facts. I'm using MOQ to create the fakes.
So my unit test looks like this:
[TestMethod]
public void ValidateAll_ValidateMethodIsInvokedOnAllFacts_WhenCalled()
{
var anyFactOne = new Mock<Fact>(); //Fact is an abstract class.
anyFactOne.Setup(f => f.Validate());
var dataWarehouseFacts = new DataWarehouseFacts { Facts = new Fact[] { anyFactOne.Object, FactGenerationHelper.GenerateRandomFact<SourceDetails>() } };
dataWarehouseFacts.ValidateAll();
}
Now I'm getting an exception because the code is actually validating the kind of Facts that can be injected to the DataWarehouseFacts class, like so:
public IEnumerable<Fact> Facts
{
get
{
.....
}
set
{
var allowedTypes = new []
{
typeof(ProductUnitFact),
typeof(FailureFact),
typeof(DefectFact),
typeof(ProcessRunFact),
typeof(CustomerFact),
typeof(ProductUnitReturnFact),
typeof(ShipmentFact),
typeof(EventFact),
typeof(ComponentUnitFact),
typeof(SourceDetails)
};
if(!value.All(rootFact => allowedTypes.Contains(rootFact.GetType())))
throw new Exception ("DataWarehouseFacts can only be set with root facts");
ProductUnitFacts = value.OfType<ProductUnitFact>().ToList();
FailureFacts = value.OfType<FailureFact>().ToList();
DefectFacts = value.OfType<DefectFact>().ToList();
ProcessRunFacts = value.OfType<ProcessRunFact>().ToList();
CustomerFacts = value.OfType<CustomerFact>().ToList();
ProductUnitReturnFacts = value.OfType<ProductUnitReturnFact>().ToList();
ShipmentFacts = value.OfType<ShipmentFact>().ToList();
EventFacts = value.OfType<EventFact>().ToList();
ComponentUnitFacts = value.OfType<ComponentUnitFact>().ToList();
SourceDetails = value.OfType<SourceDetails>().Single();
}
}
What would be the best way to get around this validation?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我想到的两个明显的方法是:
Fact
添加到允许的类型列表中。Fact
类本身。 (我认为您的 Validate() 方法是可重写的。)另一个稍微复杂的选项是在测试时注入允许类型的列表,假设您可以控制 DataWarehouseFacts 。代码>类。这可能看起来像这样:
然后只需删除
var allowedTypes = new []
位并使用this.allowedFacts
即可。The two obvious methods that leap to mind are:
Fact
to your list of allowed types.Fact
class itself. (I presume that yourValidate()
method is overrideable.)Another slightly more complicated option would be to inject your list of allowed types at test time, assuming you have control over the
DataWarehouseFacts
class. That might look something like this:Then just delete that
var allowedTypes = new []
bit and usethis.allowedFacts
instead.我会利用 Type.IsAssignableFrom
例如,而不是说
我会说
这样你就可以传递正确的子类以及精确匹配的类型。 也许,也许,这就是您所追求的类型列表本身?
I would leverage Type.IsAssignableFrom
E.g. instead of saying
I'd say
That way you can pass proper subclasses just as well as the exact matching types. Perhaps, maybe, that was what you were after with the typelist itself?
首先,我要感谢拉登奇(我确实给了他的回答+1)和塞赫的回答。尽管这并不完全是我想要的,但它们是值得记住的有趣想法。
我不能只是将 Fact 类添加到允许的类型列表中,因为这会为许多不应被允许的类打开大门;大约有30个类继承自它。
所以我最终要做的就是从他们自己的方法中的 Facts 属性的设置部分中提取代码,并将其中一个受保护的虚拟,如下所示:
这样我就能够创建一个 DataWarehouseFactsForTest 并重写 ValidateReceived 方法,这样它就可以了不会做任何事情:
这样我就能够使用 Moq 创建事实并验证私有 GetInvalidFacts 方法中的代码。例如:
First of all I want to thank both ladenedge (I did gave a +1 to his answer) and sehe for their answers. Even though it was not exactly what I was looking for they are interesting ideas to keep in mind.
I couldn't just add the Fact class to the list of allowed types since that would have opened the door for lots of classes that should not be allowed; there are about 30 classes inheriting from it.
So what I ended up doing was to extract the code from the set part of the Facts property in their own methods and made one of them protected virtual, like so:
That way I was able to create a DataWarehouseFactsForTest and override the ValidateReceived method so it wouldn't do anything:
That way I was able to to use Moq to create the Facts and verify the code within the private GetInvalidFacts method. For example: