如何有效地测试SiteCore LinkField

时间:2012-11-05 15:55:23

标签: c# sitecore sitecore6 moles

我一直在使用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。

有没有人有更好的主意?这听起来令人费解,但我认为这是这些集会的野兽的本质。

3 个答案:

答案 0 :(得分:3)

可以这样做,但你需要跳过一些箍才能使它发挥作用。

首先,有点背景:

        
  • LinkField不像其他字段类型那样从Field类继承。
  •     
  • LinkField继承自XmlField,后者继承自CustomField。
  •     
  • 通过将字段实例传递给构造函数来实例化CustomField(及其子类型)。
  •     
  • Linkfields将其值存储在此字段实例中。
  •     
  • 无法将Linkfields添加到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"];  

返回一个链接字段,并根据需要对其属性进行填充。

答案 1 :(得分:1)

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

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

使用Sitecore.FakeDb,您可以轻松地在内存中创建内容并配置所需的项目和字段。以下代码创建项 home target 主页项目有 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");
  }

答案 2 :(得分:0)

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

虽然单元测试纯粹主义者可能不同意我的方法,但我将使用Sitecore API进行单元测试以正确实例化项目。这样做是相当直接的,我刚才写了a blog post。那么你唯一的问题是处理测试数据,但使用测试设置创建测试数据相当容易,并通过测试拆除将其删除。