2012-10-24 33 views
2

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

下面是现有的两个功能:

// 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("[email protected]")] 
     public Account_Accessor(DateTime created, string createdBy); 

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

     public static Account_Accessor AttachShadow(object value); 

     [Shadowing("[email protected]")] 
     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("[email protected]")] 
     public void add_PropertyChanged(PropertyChangedEventHandler value); 
     public static ProjectBase_Accessor<T> AttachShadow(object value); 

     [Shadowing("[email protected]")] 
     public virtual void Create(); 
    } 
} 
+0

'Account'和'Account_Accessor'之间的区别是什么? –

+0

无视我的答案。因为这显然与你的其他问题的答案完全相同。 http://stackoverflow.com/a/12998986/2009。换句话说,我认为泛型实际上是你用最少的工作量来完成的方法。 – hometoast

+0

我在我的问题中添加了代码访问器代码,正如@dthorpe指出的那样,Accessor继承自BaseShadow(在它通过我们的Base类之后)。 – Josh

回答

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); 

如果客户实例的结构是足够复杂,你希望有一个共同的例行太,把特定类型的包装在公共功能的面前:

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(),但有人必须承诺选择使用哪种类型。

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

+0

感谢细节。如果我们传递一个对象并尝试转换为Account或Account_Accessor,因为这两个对象不是从同一个源继承的,我假设我们有同样的问题。 – Josh

+0

正确。您可以使参数类型为Object,但要进行方法调用,您必须将该对象转换为特定类型(Account或Account_Accessor)。由于这两种类型不相关,这意味着每次您的通用代码想要对该对象执行某些操作时,您都必须分支/拥有一条if语句。可能不值得麻烦。 – dthorpe

+0

前卫的可能性可能是尝试在常规例程中使用动态对象。动态对象是类似JavaScript工作方式的后期绑定类型。理论上,这意味着在对象上找到一个“foo”方法不会在编译时发生,它发生在运行时,所以无论涉及哪个对象实例,它都可能在运行时发现相同名称的方法。我并不熟悉动态来提供实施建议,但可能值得研究。 – dthorpe

相关问题