[翻译]NUnit---Action Attributes(八)
Attributes
NUnit 1.0使用傳統的基于繼承和命名約定來識別測試。從2.0開始NUnit使用自定義特性來實現。
因為NUnit的test fixtures不是從框架類庫繼承,所以開發人員可以用其他方式輕松的使用繼承。由于沒有任何命名約定,故名稱的選擇可以是完全面向通信測試目標。
所有NUnit特性都包含在NUnit.Framework命名空間。每個包含測試的源文件都必須聲明nunit.framework.dll程序集命名空間的引用。
從NUnit2.4.6開始,NUnit特性不再是密封的,任何繼承這些特性的特性都能夠被NUnit識別。
Action Attributes (NUnit 2.6)
NUnit設計Action特性用來更好的操作測試邏輯的組合性功能。在編寫單元測試時我們希望在測試周期中運行特定的事件的邏輯需求(e.g. SetUp, TearDown, FixtureSetUp, FixtureTearDown, etc.)。NUnit通過使用適當的特性可以執行這些事件。Action特性允許用戶創建自定義特性來封裝用于?before or after測試用例的特別操作。
The Problem of Composability
假設我們在多個fixture中都包含測試用例,并且這些需要創建或者銷毀相同的內存測試數據。我們可以創建一個fixture基類并且從它進行派生具體類。或者我們在fixture命名空間下創建一個SetUpFixture類。
This works fine, until we need some other reusable functionality, say the ability to configure or reset a ServiceLocator.?(翻譯不出來)
我們可以將這個功能放到fixture基類或者setup fixture中,但是我們要向基類混合兩種不同的責任。在使用setup fixture時,在通用命名空間只有在所有類都請求兩個特征才會有效。某些情況下我們可能不希望測試數據庫,但是我們需要ServiceLocator配置,有時又正好相反,有時又同時需要,所以我們必須使得基類是可配置的。
如果我們發現需要像配置線程CurrentPrincipal一樣使用任意方法來重用第三方功能可以快速解決問題。我們違反了單一原則并體會到了劣勢。我們期望的是分離重用的測試邏輯,然后組合為我們需要的測試。
Resolving the Problem
Action特性可以讓我們擺脫綁定,看下面的例子
View Code [TestFixture, ResetServiceLocator] public class MyTests {[Test, CreateTestDatabase]public void Test1() { /* ... */ }[Test, CreateTestDatabase, AsAdministratorPrincipal]public void Test2() { /* ... */ }[Test, CreateTestDatabase, AsNamedPrincipal("charlie.poole")]public void Test3() { /* ... */ }[Test, AsGuestPrincipal]public void Test4() { /* ... */ } }我們使用了用戶定義的特性來識別5個我們想要用不同的方式組合為不同的測試的不同操作:
- ResetServiceLocator
- CreateTestDatabase
- AsAdministratorPrincipal
- AsNamedPrincipal
- AsGuestPrincipal
我們在其他test fixtures中通過聲明合適的特性來重用。而不用從一個基類來繼承。
Implementing an Action Attribute
Action特性需要程序猿來定義。通過ITestAction接口來實現,結果定義如下:
View Code public interface ITestAction{void BeforeTest(TestDetails details);void AfterTest(TestDetails details);ActionTargets Targets { get; }}為了方便,你可以從NUnit'sTestActionAttribute派生出來,這個抽象類虛擬實現了接口的每個成員。另外,你也可以從?System.Attribute派生出來并直接實現接口。
Action Targets
當調用BeforeTest and??Targets屬性返回的確定值?。ActionTargets美劇定義如下
View Code [Flags]public enum ActionTargets{Default = 0,Test = 1,Suite = 2}當一個特性返回ActionTargets.Suite則意味著應用到一個類或者一個參數化的方法,NUnit會優先執行特性的BeforeTest方法,返回指向測試用例,然后在用例執行結束后執行AfterTest方法。這和TestFixtureSetUp?andTestFixtureTearDown特性工作方式相似。
另一方面,在相同情況下使用返回ActionTargets.Test?的特性。NUnit在執行測試是會優先執行BeforeTest方法,然后執行測試用例,最后執行AfterTest方法,這個執行方式與SetUp?and?TearDown特性的工作方式類似。
返回ActionTargets.Default?的操作會附加到特定的代碼中。當附加到一個方法時體現的就像已經指定了ActionTargets.Test?。當附加到一個類或者程序集是,體現為好像已經返回?ActionTargets.Suite值。
Test Details
?BeforeTest?and?AfterTest方法為將要運行的或者已經運行的測試提供某些信息。TestDetails類提供了如下的私有屬性,?before or after方法可以使用這些屬性來決定執行哪些操作:
- Fixture?- an object representing the user fixture, if available, or null
- Method?- the MethodInfo that implements the test, if available, or null
- FullName?- a string giving the full name of the test
- Type?- a string representing the type of the test, e.g. "Test Case"
- IsSuite?- true if the test is a suite, otherwise false
Examples
下面的示例使用相同的操作特性:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class |AttributeTargets.Interface | AttributeTargets.Assembly,AllowMultiple = true)] public class ConsoleActionAttribute : Attribute, ITestAction {private string _Message;public ConsoleActionAttribute(string message) { _Message = message; }public void BeforeTest(TestDetails details){WriteToConsole("Before", details);}public void AfterTest(TestDetails details){WriteToConsole("After", details);}public ActionTargets Targets{get { return ActionTargets.Test | ActionTargets.Suite; }}private void WriteToConsole(string eventMessage, TestDetails details){Console.WriteLine("{0} {1}: {2}, from {3}.{4}.",eventMessage,details.IsSuite ? "Suite" : "Case",_Message,fixture != null ? fixture.GetType().Name : "{no fixture}",method != null ? method.Name : "{no method}");} } View Code注意,上面的操作特性返回的是ActionTargets.Test and ActionTargets.Suite集合。可以這樣,但可能不是正常情況。這樣做了之后我們可以在多個實例中重用這個特性。這個特性的構造函數使用了一個參數:message,用于在控制臺進行輸出。Before and After方法都會通過WriteToConsole方法來進行輸出。
?
Method Attached Actions
Example 1 (applied to simple test method):
[TestFixture] public class ActionAttributeSampleTests {[Test][ConsoleAction("Hello")]public void SimpleTest(){Console.WriteLine("Test ran.");} } Console Output:Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test ran.After Case: Hello, from ActionAttributeSampleTests.SimpleTest. View CodeExample 2 (applied action twice to test method):
[TestFixture] public class ActionAttributeSampleTests {[Test] [ConsoleAction("Hello")][ConsoleAction("Greetings")]public void SimpleTest(){Console.WriteLine("Test run.");} } Console Output:Before Case: Greetings, from ActionAttributeSampleTests.SimpleTest.Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test run.After Case: Hello, from ActionAttributeSampleTests.SimpleTest.After Case: Greetings, from ActionAttributeSampleTests.SimpleTest. View Code?
Remarks
? 允許相同的特性應用多次。注意盡管這個以穩定的.NET版本,但是特性應用的順序是不確定的。?
Example 3 (applied to a test method with test cases):
[TestFixture] public class ActionAttributeSampleTests {[Test] [ConsoleAction("Hello")][TestCase("02")][TestCase("01")]public void SimpleTest(string number){Console.WriteLine("Test run {0}.", number);} } Console Output:Before Suite: Hello, from ActionAttributeSampleTests.SimpleTest.Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test run 01.After Case: Hello, from ActionAttributeSampleTests.SimpleTest.Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test run 02.After Case: Hello, from ActionAttributeSampleTests.SimpleTest.After Suite: Hello, from ActionAttributeSampleTests.SimpleTest. View CodeRemarks
當一個方法應用了一個或者多個TestCase特性時,NUnit會將這個方法當作一個?test suite。你會注意到BeforeTest會在suite運行前執行一次,AfterTest會在運行后執行一次。另外,BeforeTest and AfterTest會在每個測試用例再執行一次。注意測試用例的執行順序是不確定的。
Type Attached Actions
Example 1:
[TestFixture] [ConsoleAction("Hello")] public class ActionAttributeSampleTests {[Test]public void SimpleTestOne(){Console.WriteLine("Test One.");}[Test]public void SimpleTestTwo(){Console.WriteLine("Test Two.");} } Console Output:Before Suite: Hello, from ActionAttributeSampleTests.{no method}.Before Case: Hello, from ActionAttributeSampleTests.SimpleTestOne.Test ran.After Case: Hello, from ActionAttributeSampleTests.SimpleTestOne.Before Case: Hello, from ActionAttributeSampleTests.SimpleTestTwo.Test ran.After Case: Hello, from ActionAttributeSampleTests.SimpleTestTwo.After Suite: Hello, from ActionAttributeSampleTests.{no method}. View CodeRemarks
在這個測試中,這個類是test suite,BeforeTest and AfterTes在這個類構造時執行一次,在每個用例再執行一次。
Example 2 (attached to interface):
[ConsoleAction("Hello")] public interface IHaveAnAction { }[TestFixture] public class ActionAttributeSampleTests : IHaveAnAction {[Test] public void SimpleTest(){Console.WriteLine("Test run.");} } Console Output:Before Suite: Hello, from ActionAttributeSampleTests.{no method}.Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test run.After Case: Hello, from ActionAttributeSampleTests.SimpleTest.After Suite: Hello, from ActionAttributeSampleTests.{no method}. View CodeRemarks
操作特性可以應用于接口。如果標記為TestFixture的類實現了這個接口,那么這個類會從接口繼承這個操作特性,就像你在這個類本身應用了操作特性。
Example 3 (action attribute is applied to interface and attribute uses interface to provide data to tests):
[AttributeUsage(AttributeTargets.Interface)] public class InterfaceAwareActionAttribute : TestActionAttribute {private readonly string _Message;public InterfaceAwareActionAttribute(string message) { _Message = message; }public override void BeforeTest(TestDetails details){IHaveAnAction obj = details.Fixture as IHaveAnAction;if(obj != null)obj.Message = _Message;}public override ActionTargets Target{get { return ActionTargets.Test; }} }[InterfaceAwareAction("Hello")] public interface IHaveAnAction { string Message { get; set; } }[TestFixture] public class ActionAttributeSampleTests : IHaveAnAction {[Test] public void SimpleTest(){Console.WriteLine("{0}, World!", Message);}public string Message { get; set; } } View CodeConsole Output:
Hello, World!Remarks
這里我們看到一個新的操作特性:InterfaceAwareAction。這個特性在BeforeTest方法中使用TestDetails的Fixture屬性并強制轉換為IHaveAnAction接口。如果這個fixture實現了IHaveAnAction接口,那么會將這個特性在構造函數時傳入的string賦值給Message屬性。因為這個特性應用于接口,任何實現了這個接口的類都會將構造函數輸入的sring賦值為Message屬性。在操作特性為測試用例提供數據、服務時非常有用。
注意這個特性從TestActionAttribute繼承。它使用默認的AfterTest,并且重寫了BeforeTest?and?Target。
Assembly Attached Action
Example 1:
[assembly: ConsoleAction("Hello")][TestFixture] public class ActionAttributeSampleTests {[Test] public void SimpleTest(){Console.WriteLine("Test run.");} } Console Output:Before Suite: Hello, from {no fixture}.{no method}.Before Case: Hello, from ActionAttributeSampleTests.SimpleTest.Test run.After Case: Hello, from ActionAttributeSampleTests.SimpleTest.After Suite: Hello, from {no fixture}.{no method}. View CodeRemarks
這個示例中的ConsoleAction特性是應用于整個程序集,NUnit會將整個程序集當作一個test suite (in fact, a suite of suites)。由于ConsoleAction特性實現了?ITestSuiteAction and ITestCaseAction兩個接口,NUnit會程序集運行任何測試用例之前執行BeforeTest,運行所有測試用例之后執行AfterTest。另外,在構建保障清除狀態、防止某個測試用例影響其他測試用例輸出的操作特性時時非常有用的。例如,你可以使用操作特性來清除靜態或者緩存的數據、者服務。
?
哎!翻譯的很痛苦,自己都覺得不倫不類的,但是為了進步為了將來還是堅持下去。
Fighting!
?
轉載于:https://www.cnblogs.com/kim01/archive/2013/05/15/3024973.html
總結
以上是生活随笔為你收集整理的[翻译]NUnit---Action Attributes(八)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mvc中循环遍历分配的代码
- 下一篇: 自已动手写控件 --- 我第一次学习写自