2013-04-11 10 views
43

ho una classe di base per i miei test che si compone nel seguente modo:MSTest ClassInitialize ed ereditarietà

[TestClass] 
public abstract class MyBaseTest 
{ 
    protected static string myField = ""; 

    [ClassInitialize] 
    public static void ClassInitialize(TestContext context) 
    { 
     // static field initialization 
     myField = "new value"; 
    } 
} 

Ora sto cercando di creare un nuovo test che eredita dalla base, con la seguente firma:

[TestClass] 
public class MyTest : MyBaseTest 
{ 
    [TestMethod] 
    public void BaseMethod_ShouldHave_FieldInitialized() 
    { 
     Assert.IsTrue(myField == "new value"); 
    } 
} 

il ClassInitialize non viene mai chiamato dai test bambino ... Qual è la reale e corretta modo di usare l'inizializzazione di test con l'ereditarietà sul MSTest?

+0

duplicati (preoccupazioni trasversali, che solo così capita di richiedere esattamente le stesse impostazioni per ogni classe di prova e metodo di prova.): Http: //stackoverflow.com/questions/7322154/classinitialize-attribute-in-unit-test-based-class-not-called –

risposta

27

Sfortunatamente non è possibile ottenere questo in quanto il ClassInitializeAttribute Class non può essere ereditato.

Un attributo ereditato può essere utilizzato dalle sottoclassi delle classi che lo utilizzano. Poiché non è possibile ereditare il valore ClassInitializeAttribute, quando la classe MyTest viene inizializzata, non è possibile chiamare il metodo dalla classe MyBaseTest.

Provare a risolverlo con un altro modo. Un modo meno efficiente consiste nel definire nuovamente il metodo ClassInitialize in MyTest e chiamare semplicemente il metodo di base anziché duplicare il codice.

+22

Ho provato il tuo approccio e funziona, ma sinceramente Microsoft dovrebbe risolvere il problema perché NUnit non ha questo comportamento . – Raffaeu

+0

Quando si dice "la classe ClassInitializeAttribute non può essere ereditata", ci si riferisce alla classe che è 'sealed'? -that non dovrebbe influire sul modo in cui l'attributo appare quando applicato a un metodo ereditato ... – binki

+0

Poiché non imposta' AttributeUsageAttribute. Ereditato' su 'false', sarà ereditato ... – binki

0

Sappiamo che una nuova istanza della classe viene costruita per ogni [TestMethod] nella classe mentre viene eseguita. Il costruttore parametrico della classe base verrà chiamato ogni volta che ciò accade. Non potresti semplicemente creare una variabile statica nella classe base e testarla quando viene eseguito il costruttore?

Questo aiuta a non dimenticare di inserire il codice di inizializzazione nella sottoclasse.

Non so se c'è qualche svantaggio di questo approccio ...

Come così:

public class TestBase 
{ 
    private static bool _isInitialized = false; 
    public TestBase() 
    { 
     if (!_isInitialized) 
     { 
      TestClassInitialize(); 
      _isInitialized = true; 
     } 
    } 

    public void TestClassInitialize() 
    { 
     // Do one-time init stuff 
    } 
} 
public class SalesOrderTotals_Test : TestBase 
{ 
    [TestMethod] 
    public void TotalsCalulateWhenThereIsNoSalesTax() 
    { 
    } 
    [TestMethod] 
    public void TotalsCalulateWhenThereIsSalesTax() 
    { 
    } 
} 
+2

You don ' t ottenere TestContext con questa soluzione. È un peccato. – Visser

+0

Inoltre, non hai CleanUp – bubi

3

Una potenziale soluzione è quello di definire una nuova classe con AssemblyInitializeAttribute invece. Ha una portata diversa, ovviamente, ma per me soddisfa le mie esigenze

using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace MyTests 
{ 
    [TestClass] 
    public sealed class TestAssemblyInitialize 
    { 
    [AssemblyInitialize] 
    public static void Initialize(TestContext context) 
    { 
     ... 
    } 
    } 
} 
+0

Questo è quello che ho fatto anch'io, ma sono curioso; perché il tuo test di base è 'sigillato'? Questo non ti impedirà di ereditare quella classe e di fare esattamente quello che hai deciso di fare? –

+0

Non lo sto usando per ereditarietà. Tutte le mie vere classi di test derivano da una diversa classe base.Questa classe è puramente per il metodo 'AssemblyInitialize'. È solo una decisione progettuale che ho fatto per tenere separate le cose. –