4

我一直在使用 moles 对代码中难以触及的部分编写单元测试——特别是在我使用 sitecore 类库中的类型(这些类型很难与模拟框架一起使用)的情况下。我遇到了一个令人惊讶的棘手问题,我正在尝试使用 LinkField 类型并测试以下类型的代码片段。

LinkField linkField = item.Fields["External Url"];

if (linkField.IsInternal && linkField.TargetItem != null)
{
// want to test this path

item.Fields 是一个字段集合,其索引器返回 SiteCore.Data.Field 类型,但 LinkField 设置为使用隐式运算符隐藏转换,这意味着您可以在代码中使用 LinkField 的实例。

我的困难是我不能创建MLinkField 类型的mole,并将其分配给Item 中的FieldCollection,因为它是强类型的Field。此外,似乎虽然我能够创建一种 MField,但当隐式转换发生时,它将工作并返回一个对象,但没有一个字段被设置为具有任何值。这意味着,我无法测试上面依赖于以某种方式设置的 linkField 状态的代码路径。

我能想到的设置这些值的唯一方法是间接的——即通过分析隐式转换并在 MField 中设置这些值来找到需要设置的值。隐式运算符调用 LinkField 构造函数,如下所示:

public LinkField(Field innerField) : base(innerField, "link")

这意味着我需要注意它如何实例化基类型 (XmlField),以及该类的基类型 (CustomField)。然后,查看 TargetItem 正在寻找的基础值。最终结果是需要淘汰:

InnerField.Database.Items[internalPath];

或者

InnerField.Database.Items[targetID];

InnerField 实际上是我的 MField。

有没有人有更好的主意?这听起来非常令人费解,但我认为这是拥有这些组件的野兽的本性。

4

3 回答 3

3

它可以完成,但您需要跳过几个圈才能使其发挥作用。

首先,有一点背景:

  • LinkField 不像其他一些字段类型那样从 Field 类继承。
  • LinkField 继承自 XmlField,而 XmlField 又继承自 CustomField。
  • CustomField(及其子类型)通过将字段实例传递给构造函数来实例化。
  • 链接字段将其值存储在此字段实例中。
  • 不能将链接字段添加到 FieldCollection,因为它们不是从 Field 继承的。
  • 我们需要添加 LinkField 的 InnerField 属性,而不是添加 LinkField。
  • 当从 FieldCollection 中提取 Field 并将其分配给 LinkField 时,将执行隐式转换操作。
  • 隐式转换操作通过将所选字段传递给 LinkField 的构造函数来创建一个新的 LinkField。返回的是这个新的 LinkField。

现在一些代码:

const string externalUrl = "External Url";
const string targetItemName = "Target Item";                     
Field field = new ShimField { IDGet = () => ID.NewID, NameGet = () => externalUrl };
Item targetitem = new ShimItem { IDGet = () => ID.NewID, NameGet = () => targetItemName };
LinkField linkfield = new ShimLinkField(field) { IsInternalGet = () => true, TargetItemGet = () => targetitem };                         
ShimLinkField.ImplicitOpFieldLinkField = (f) => linkfield;
FieldCollection fields = new ShimFieldCollection { ItemGetString = (name) => linkfield.InnerField };
Item item = new ShimItem { NameGet = () => "Test Item", FieldsGet = () => fields };

现在来解释一下:

使上述代码工作的关键是这一行:

ShimLinkField.ImplicitOpFieldLinkField = (f) => linkfield;

通过 Shimming 隐式转换运算符,我们可以确保在调用以下行时:

LinkField linkField = item.Fields["External Url"];  

返回一个链接字段,并且它的属性根据您的需要填充。

于 2014-01-14T12:08:11.330 回答
1

由于LinkField无法以正确的方式模拟它,因此必须“按原样”在单元测试中使用它,这意味着您必须同时测试代码和链接字段的实现。

好消息是 Link 字段是使用XmlField存储在内部字段值中的 xml 数据进行操作的。为了配置链接字段行为,您只需要在值中设置正确的 xml。

使用Sitecore.FakeDb,您可以轻松地在内存中创建内容并配置您需要的项目和字段。以下代码创建项目hometarget主页项具有指向目标项的Url链接字段:TargetId

  ID targetId = ID.NewID;

  using (var db = new Db
    {
      new DbItem("home")
        {
          // Field 'Url' is an internal link which targets the 'target' item
          { "Url", "<link linktype=\"internal\" id=\"{0}\" />".FormatWith(targetId) }
        },
      new DbItem("target", targetId)
    })
  {
    Item item = db.GetItem("/sitecore/content/home");
    LinkField linkField = item.Fields["Url"];

    linkField.IsInternal.Should().BeTrue();
    linkField.TargetItem.Should().NotBeNull();
    linkField.TargetItem.Paths.FullPath.Should().Be("/sitecore/content/target");
  }
于 2015-01-18T13:01:22.683 回答
0

就个人而言,我确实尝试将 Sitecore 从不需要直接与其交互的业务逻辑中抽象出来;但是我注意到有些事情需要直接处理,我尽量不要走得太远。如果您花费太多时间将 Sitecore 从您的逻辑中抽象出来,那么在我看来,您最终可能会使利用一些使 Sitecore 有用的功能变得更加困难。

虽然单元测试纯粹主义者可能不同意我的方法,但我将进行使用 Sitecore API 正确实例化项目的单元测试。这样做相当简单,我不久前写了一篇关于它的博客文章。那么您唯一的问题是处理测试数据,但是使用测试设置创建它并通过测试拆卸将其删除是相当容易的。

于 2013-12-16T20:12:07.107 回答