2

如果您需要测试来自另一个项目的非公共属性,Microsoft 单元测试向导会创建 Accessor 对象。在我的单元测试中,我创建了辅助函数,这样我就不会在每个单元测试方法中重复相同的代码。目前我有两个几乎相同的测试,除了一个采用标准公共对象,另一个采用 Accessor 版本。由于访问器基于公共对象,我应该能够拥有一个功能。我曾假设我可以使用泛型来完成一个简单的演员表。但在发布问题后,我发现需要做更多的工作,包括必须更新底层对象。我的问题是另一种方法可以将这些冗余方法减少到只有一个使用强制转换(或另一种)方法的功能?

以下是现有的两个功能:

// Common function to create a new test record with standard Account object
internal static void CreateAccount(out Account account, bool saveToDatabase)
{
    DateTime created = DateTime.Now;
    string createdBy = _testUserName;

    account = new Account(created, createdBy);

    account.Notes = Utilities.RandomString(1000);

    if (saveToDatabase)
        account.Create();
}

// Common function to create a new test record with Account_Accessor
internal static void CreateAccount(out Account_Accessor account, bool saveToDatabase)
{
    DateTime created = DateTime.Now;
    string createdBy = _testUserName;

    account = new Account_Accessor(created, createdBy);

    account.Notes = Utilities.RandomString(1000);

    if (saveToDatabase)
        account.Create();
}

我有两打这样的单元测试,真实对象平均有 10 个属性,我在这里简化了示例。

这是单元测试 API 创建的访问器代码(再次,我已将其缩减以简化示例):

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.ObjectModel;
using System.Data;

namespace NameHere.Bll
{
    [Shadowing("NameHere.Bll.Account")]
    public class Account_Accessor : ProjectBase_Accessor<Account>
    {
        protected static PrivateType m_privateType;

        public Account_Accessor(PrivateObject value);
        [Shadowing(".ctor@2")]
        public Account_Accessor(DateTime created, string createdBy);

        [Shadowing("_notes")]
        public string _notes { get; set; }

        public static Account_Accessor AttachShadow(object value);

        [Shadowing("Create@0")]
        public override void Create();
    }
}

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace NameHere.Bll
{
    [Shadowing("NameHere.Bll.ProjectBase`1")]
    public class ProjectBase_Accessor<T> : BaseShadow, INotifyPropertyChanged
    {
        protected static PrivateType m_privateType;

        public ProjectBase_Accessor(PrivateObject value);

        [Shadowing("Created")]
        public DateTime Created { get; set; }
        public static PrivateType ShadowedType { get; }

        [Shadowing("add_PropertyChanged@1")]
        public void add_PropertyChanged(PropertyChangedEventHandler value);
        public static ProjectBase_Accessor<T> AttachShadow(object value);

        [Shadowing("Create@0")]
        public virtual void Create();
    }
}
4

1 回答 1

3

问题是即使访问器类公开了与它所隐藏的类相同的方法和属性,但访问器和原始类之间没有公共接口。Account_Accessor 继承自 BaseShadow,Account 继承自其他东西。就编译器而言,它们是完全不相关的类型,不兼容赋值,因此很难将每个类型的实例传递到公共例程中。

如果您可以强制 Account_Accessor 类实现同样由 Account 实现的接口类型,那么您可以将每个实例的实例传递给以接口类型作为参数的函数。像这样:

internal static IAccount SetupAccount(IAccount account, bool saveToDatabase)
{
// do account setup here - not construction
}

// to call: construct instance, then pass to common function
var account = new Account(a, b);
SetupAccount(account, true);

如果 Account 实例的构造足够复杂以至于您也希望有一个通用例程,请将特定类型的包装器放在通用函数前面:

internal static IAccount CreateAccount(bool saveToDatabase)
{
    var account = new Account(a,b);
    return SetupAccount(account, saveToDatabase);
}

internal static IAccount CreateAccountAccessor(bool saveToDatabase)
{
    var account = new Account_Accessor(a,b);
    return SetupAccount(account, saveToDatabase);
}

你无法逃避的一点是:某个地方的某个人必须承诺要构造哪个实例。即使您将其归结为传递类型和使用Activator.CreateInstance(),也必须有人承诺选择要使用的类型。

一旦构造了实例,并且两种类型都实现了一个通用接口,那么所有需要关心的通用功能就是通用接口。

于 2012-10-24T17:48:12.037 回答