在设计时代码中使用模拟框架的缺点
本文关键字:模拟 框架 缺点 时代 代码 | 更新日期: 2023-09-27 18:34:26
我正在为我的公司开发一个棱镜模板。 我想使设计时数据尽可能容易地使用。
为此,我正在创建一个继承自我 ViewModel 的设计类。 DesignViewModel 将调用我的 ViewModel 的构造函数。
我的 ViewModel 构造函数有时会调用棱镜方法和属性,如 IRegionManager.Regions
。 我正在考虑为将使用 NSubstitute 的接口制作一个存根。 喜欢这个:
public class DesginRegionManager : IRegionManager
{
public IRegionManager CreateRegionManager(){return this;}
private readonly IRegionCollection regions;
public DesginRegionManager()
{
regions = Substitute.For<IRegionCollection>(); <--------
} |
|
public IRegionCollection Regions {get { return regions; }} |
} |
|
// Using NSubstitute here ----------------------------------------
我打算为IUnityContainer
做类似的事情.
除了单元测试之外,我从未在任何事情中使用过 Mocking 框架。 这样使用它是个坏主意吗?
虽然它永远不会在生产代码中调用,但它将为我的实际应用程序添加对 NSubstitute 的依赖。
这样做有缺点吗?
在继续之前,我对 Prism 的经验很少,尽管我以前使用过 caliburn,所以我将假设它以类似的方式工作。但我确实有一点经验。
出于设计目的使用替代品是一个坏主意吗?
我的简短回答是否定的。
并不完全理想,但是当您有一个设计部门(或在您继续编码时负责UI元素的人(将UI执行的代码与设计过程隔离开来是一种很好的做法,因为即使要实时执行的代码有问题,您也可以通过界面进行改进。
当我说替代品时,我指的是通过Visual Studio或blend在设计时创建的真实(示例数据(实例。(我将在最后提供一个示例(。
嘲笑这些元素是个坏主意吗?
也许不是,尽管您将在项目中包含更多依赖项,这些依赖项对于项目的执行并不那么重要。
积极的一面是:
模拟定义意味着您将维护更少的代码,但您将无法模拟这些虚假实例上的数据(或者除非您开始为每个模拟返回示例数据,否则不会(。
消极的一面:
首先,根据模拟框架,您将加载(在设计时(动态创建的代理或假实例,以模拟拦截调用和其他内容的某些行为。如果您创建的大量对象不如实际代码轻量级,这可能是一个痛苦的操作。
除了你的设计师(如果有一个混合大师(将不得不收集地狱nsubstitute(或你喜欢的模拟框架(如何工作的知识。
如果我有一个设计器上限,并且您向我展示了一个具有特定定义的类,我可以复制该类,而不是使用称为 substitute 的外部库定义的类,我可能会告诉您我更喜欢第一个选项,因为我不需要学习任何其他东西除了一点 c#。即使这样,我也不会尝试触摸该代码。
在您的项目中添加 nreplace 有什么缺点吗?
只要您正确使用 DesignInstance,并且带有模拟的代码引用与实际功能隔离,并且您不尝试替换非虚拟属性,或具有多个参数或密封类的实例(基本上对替换内容的任何限制(,您就会没问题,因为设计属性将在代码的特殊模式下执行。
特别是 NSubstitute 可能会在尝试创建实例或访问某些属性时引发异常,在这种情况下,IDE 可能会在加载实例时崩溃或使控件崩溃。
有样品吗?
你可能知道该写什么,但它就在这里(虽然不是棱镜(。
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/winfx/expression/blend/2008"
xmlns:vm="clr-namespace:Project.Namespace.ViewModels.DataSamples"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type={x:Type NationViewModelSampleData}}">
<StackPanel DataContext="{Binding Leader}">
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</UserControl>
NationViewModel实现了这样的接口
namespace Project.Namespace.ViewModels
{
public interface INationViewModel
{
IPerson Leader { get; }
IEnumerable<IPerson> Citizens { get; }
}
public interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
}
示例数据为:
namespace Project.Namespace.ViewModels.DataSamples
{
public class NationViewModelSampleData : Project.Namespace.ViewModels.INationViewModel
{
public NationViewModelSampleData()
{
// You could have done something like this as well, but for this sample was more code to write.
// Leader = Substitute.For<IPerson>();
// Leader.FirstName.Returns("John");
// Leader.LastName.Returns("Doe");
// or just...
Leader = new Person { FirstName = "John", LastName = "Doe" };
Citizens = new IPerson[]
{
new Person { FirstName = "Malcolm", LastName = "Little" },
Leader
};
// You could have applied the mock to this section as well... again, more code for this scenario than just a simple real instance.
}
public IPerson Leader { get; private set; }
public IEnumerable<IPerson> Citizens { get; private set; }
}
}
在这个示例中,我决定使用实际实例,因为我想象中的 Person 实现非常小且使用安全......但我希望它的复杂性会增加,我可能也想减少这种松弛,只实现我的 PersonSampleData 实例或像您之前描述的那样模拟它。
希望此评论对您有所帮助。
干杯!