OSDN Git Service

3663295368aea833360fc7c30cb5203acf704f8e
[wptscs/wpts.git] / WptscsTest / PrivateAccessor.cs
1 // ================================================================================================
2 // <summary>
3 //      NUnitテスト支援用クラスソース。</summary>
4 //
5 // <copyright file="PrivateAccessor.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Tests
12 {
13     using System;
14     using System.Collections.Generic;
15     using System.Reflection;
16     using NUnit.Framework;
17
18     /// <summary>
19     /// NUnitでのテスト用に private, protected といった外部からアクセスできないメソッドを実行するためのクラス。
20     /// クラス・オブジェクトに被せるように使用する。
21     /// </summary>
22     /// <typeparam name="T">ラップするオブジェクトのクラス。</typeparam>
23     /// <remarks>使い方については、概ねJUnitのPrivateAccessorと同じようなイメージ。</remarks>
24     public class PrivateAccessor<T>
25     {
26         #region private変数
27
28         /// <summary>
29         /// オブジェクトを生成する際に使用するコンストラクタ。
30         /// </summary>
31         private ConstructorInfo constructor;
32
33         /// <summary>
34         /// テストするメソッド。
35         /// </summary>
36         private MethodInfo method;
37
38         #endregion
39
40         #region コンストラクタ
41
42         /// <summary>
43         /// 指定されたオブジェクトをテストするためのインスタンスを生成。
44         /// </summary>
45         /// <param name="obj">オブジェクト。</param>
46         public PrivateAccessor(T obj)
47         {
48             this.Instance = obj;
49         }
50
51         /// <summary>
52         /// 新しいオブジェクトをテストするためのインスタンスを生成。
53         /// </summary>
54         public PrivateAccessor()
55         {
56         }
57
58         #endregion
59
60         #region プロパティ
61
62         /// <summary>
63         /// テストする/したクラスのオブジェクト。
64         /// </summary>
65         public T Instance
66         {
67             get;
68             set;
69         }
70
71         #endregion
72
73         #region メソッド
74
75         /// <summary>
76         /// テストするクラスのコンストラクタを指定する。
77         /// </summary>
78         /// <param name="parameterTypes">パラメータ配列。</param>
79         /// <remarks>コンストラクタの確保に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。</remarks>
80         public void SetConstructor(params Type[] parameterTypes)
81         {
82             try
83             {
84                 // 指定されたコンストラクタを取得
85                 this.constructor = typeof(T).GetConstructor(
86                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
87                     null,
88                     parameterTypes,
89                     null);
90                 if (this.constructor == null)
91                 {
92                     // メソッド取得失敗のためテスト失敗
93                     Assert.Fail("Constructor is not found");
94                 }
95             }
96             catch (Exception e)
97             {
98                 // メソッド指定誤り、アクセス不可も含め全てテスト失敗
99                 Assert.Fail(e.Message);
100             }
101         }
102
103         /// <summary>
104         /// テストするクラスのオブジェクトを指定されたパラメータで生成する。
105         /// </summary>
106         /// <param name="initargs">コンストラクタ呼び出しに引数として渡すオブジェクトの配列。</param>
107         /// <returns>このオブジェクトが表すコンストラクタを呼び出すことで作成される新規オブジェクト。</returns>
108         /// <remarks>
109         /// コンストラクタには、<see cref="SetConstructor"/>で指定されたメソッドを用いる。
110         /// 指定されていない場合は、デフォルトコンストラクタを使用する。
111         /// オブジェクトの生成に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。
112         /// </remarks>
113         public T NewInstance(params object[] initargs)
114         {
115             try
116             {
117                 // コンストラクタが指定されておらず、パラメータも0件の場合
118                 if (this.constructor == null
119                         && (initargs == null || initargs.Length == 0))
120                 {
121                     // デフォルトコンストラクタを使用する
122                     this.SetConstructor();
123                 }
124
125                 // 指定されているコンストラクタでオブジェクトを生成
126                 this.Instance = (T)this.constructor.Invoke(initargs);
127
128                 // 生成したオブジェクトを返す
129                 return this.Instance;
130             }
131             catch (TargetInvocationException e)
132             {
133                 // コンストラクタが例外を投げる場合は、そのまま返す
134                 throw e.InnerException;
135             }
136             catch (Exception e)
137             {
138                 // オブジェクトが生成できない場合、テスト失敗
139                 Assert.Fail(e.Message);
140
141                 // failの時点で処理は終了する、コンパイルエラー抑止
142                 throw new Exception("Accessor error", e);
143             }
144         }
145
146         /// <summary>
147         /// テストするメソッドを指定する。
148         /// </summary>
149         /// <param name="name">メソッドの名前。</param>
150         /// <param name="parameterTypes">パラメータ配列。</param>
151         /// <remarks>メソッドの確保に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。</remarks>
152         public void SetMethod(string name, params Type[] parameterTypes)
153         {
154             try
155             {
156                 // 指定されたメソッドを取得
157                 this.method = typeof(T).GetMethod(
158                     name,
159                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
160                     null,
161                     parameterTypes,
162                     null);
163                 if (this.method == null)
164                 {
165                     // メソッド取得失敗のためテスト失敗
166                     Assert.Fail(name + " is not found");
167                 }
168             }
169             catch (Exception e)
170             {
171                 // メソッド指定誤り、アクセス不可も含め全てテスト失敗
172                 Assert.Fail(e.Message);
173             }
174         }
175
176         /// <summary>
177         /// 指定されているメソッドを実行する。
178         /// </summary>
179         /// <param name="args">メソッド呼び出しに使用される引数。</param>
180         /// <returns>このオブジェクトが表すメソッドを、パラメータ<paramref name="args"/>を使用して実行した結果。</returns>
181         /// <remarks>
182         /// staticメソッド以外の場合必要な呼び出し元オブジェクトには、
183         /// <see cref="NewInstance"/>または<see cref="Instance"/>
184         /// で指定されたオブジェクトを用いる。
185         /// 指定されていない場合は、可能であればデフォルトコンストラクタでオブジェクトを生成する。
186         /// メソッドの実行に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。
187         /// </remarks>
188         public object Invoke(params object[] args)
189         {
190             try
191             {
192                 // オブジェクトが指定されておらず、非staticメソッドの場合
193                 if (this.Instance == null && !this.method.IsStatic)
194                 {
195                     // デフォルトコンストラクタでオブジェクトを作成する
196                     // ※ コンストラクタが指定されている場合、例外
197                     this.NewInstance();
198                 }
199
200                 // 指定されているオブジェクトでメソッドを実行
201                 return this.method.Invoke(this.Instance, args);
202             }
203             catch (TargetInvocationException e)
204             {
205                 // メソッドが例外を投げる場合は、そのまま返す
206                 throw e.InnerException;
207             }
208             catch (Exception e)
209             {
210                 // メソッド未設定、型違いも含めて全てテスト失敗
211                 Assert.Fail(e.Message);
212
213                 // failの時点で処理は終了する、コンパイルエラー抑止
214                 throw new Exception("Accessor error", e);
215             }
216         }
217
218         /// <summary>
219         /// テストする/したクラスのオブジェクトより、指定されたフィールドの値を返す。
220         /// </summary>
221         /// <param name="name">フィールド名。</param>
222         /// <returns>フィールドの値。</returns>
223         /// <remarks>値の取得に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。</remarks>
224         public object GetField(string name)
225         {
226             try
227             {
228                 // 指定されたフィールドを取得
229                 FieldInfo field = typeof(T).GetField(
230                     name,
231                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
232                 if (field == null)
233                 {
234                     // メソッド取得失敗のためテスト失敗
235                     Assert.Fail(name + " is not found");
236                 }
237
238                 // 指定された値をフィールドから取得する
239                 return field.GetValue(this.Instance);
240             }
241             catch (Exception e)
242             {
243                 // フィールド指定誤り、アクセス不可、オブジェクト無しも含め全てテスト失敗
244                 Assert.Fail(e.Message);
245
246                 // failの時点で処理は終了する、コンパイルエラー抑止
247                 throw new Exception("Accessor error", e);
248             }
249         }
250
251         /// <summary>
252         /// テストするクラスのオブジェクトで、指定されたフィールドに値を設定する。
253         /// </summary>
254         /// <param name="name">フィールド名。</param>
255         /// <param name="value">新しいフィールド値。</param>
256         /// <remarks>
257         /// staticフィールド以外の場合に必要な設定先オブジェクトには、
258         /// <see cref="NewInstance"/>または<see cref="Instance"/>
259         /// で指定されたオブジェクトを用いる。
260         /// 指定されていない場合は、可能であればデフォルトコンストラクタでオブジェクトを生成する。
261         /// 値の設定に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。
262         /// </remarks>
263         public void SetField(string name, object value)
264         {
265             try
266             {
267                 // 指定されたフィールドを取得
268                 FieldInfo field = typeof(T).GetField(
269                     name,
270                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
271                 if (field == null)
272                 {
273                     // メソッド取得失敗のためテスト失敗
274                     Assert.Fail(name + " is not found");
275                 }
276
277                 // オブジェクトが指定されておらず、非staticフィールドの場合
278                 if (this.Instance == null && !field.IsStatic)
279                 {
280                     // デフォルトコンストラクタでオブジェクトを作成する
281                     // ※ コンストラクタが指定されている場合、例外
282                     this.NewInstance();
283                 }
284
285                 // 指定された値をフィールドに設定する
286                 field.SetValue(this.Instance, value);
287             }
288             catch (Exception e)
289             {
290                 // フィールド指定誤り、アクセス不可も含め全てテスト失敗
291                 Assert.Fail(e.Message);
292             }
293         }
294
295         /// <summary>
296         /// テストする/したクラスのオブジェクトより、指定されたプロパティの値を返す。
297         /// </summary>
298         /// <param name="name">プロパティ名。</param>
299         /// <param name="index">インデックスプロパティの場合のインデックス。</param>
300         /// <returns>プロパティの値。</returns>
301         /// <remarks>値の取得に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。</remarks>
302         public object GetProperty(string name, params object[] index)
303         {
304             try
305             {
306                 // 指定されたプロパティを取得
307                 PropertyInfo property = typeof(T).GetProperty(
308                     name,
309                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
310                 if (property == null)
311                 {
312                     // メソッド取得失敗のためテスト失敗
313                     Assert.Fail(name + " is not found");
314                 }
315
316                 // 指定された値をプロパティから取得する
317                 return property.GetValue(this.Instance, index);
318             }
319             catch (Exception e)
320             {
321                 // プロパティ指定誤り、アクセス不可、オブジェクト無しも含め全てテスト失敗
322                 Assert.Fail(e.Message);
323
324                 // failの時点で処理は終了する、コンパイルエラー抑止
325                 throw new Exception("Accessor error", e);
326             }
327         }
328
329         /// <summary>
330         /// テストするクラスのオブジェクトで、指定されたプロパティに値を設定する。
331         /// </summary>
332         /// <param name="name">プロパティ名。</param>
333         /// <param name="value">新しいプロパティ値。</param>
334         /// <param name="index">インデックスプロパティの場合のインデックス。</param>
335         /// <remarks>
336         /// 設定先オブジェクトには<see cref="NewInstance"/>または<see cref="Instance"/>
337         /// で指定されたオブジェクトを用いる。
338         /// 指定されていない場合は、可能であればデフォルトコンストラクタでオブジェクトを生成する。
339         /// 値の設定に失敗した場合は、<see cref="Assert.Fail()"/>にてテストを失敗させる。
340         /// </remarks>
341         public void SetProperty(string name, object value, params object[] index)
342         {
343             try
344             {
345                 // 指定されたプロパティを取得
346                 PropertyInfo property = typeof(T).GetProperty(
347                     name,
348                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
349                 if (property == null)
350                 {
351                     // メソッド取得失敗のためテスト失敗
352                     Assert.Fail(name + " is not found");
353                 }
354
355                 // オブジェクトが指定されていない場合
356                 if (this.Instance == null)
357                 {
358                     // デフォルトコンストラクタでオブジェクトを作成する
359                     // ※ コンストラクタが指定されている場合、例外
360                     this.NewInstance();
361                 }
362
363                 // 指定された値をプロパティに設定する
364                 property.SetValue(this.Instance, value, index);
365             }
366             catch (TargetInvocationException e)
367             {
368                 // メソッドが例外を投げる場合は、そのまま返す
369                 throw e.InnerException;
370             }
371             catch (Exception e)
372             {
373                 // プロパティ指定誤り、アクセス不可も含め全てテスト失敗
374                 Assert.Fail(e.Message);
375             }
376         }
377
378         #endregion
379     }
380 }