2013-10-07 9 views
5

Ich habe eine große Anwendung, die einige Parameter aus einer Konfigurationsdatei liest.Ändern eines privaten statischen Readonly-Feldes

Ich schreibe Unit-Tests für eine Klasse, die ein Ergebnis nach der Durchführung bestimmte Operationen sowohl mit einem Parameter und einem Wert aus der Konfigurationsdatei gelesen erzeugt:

internal static class Password 
{ 
    private static readonly byte PASSWORD_PRIVATE_KEY 
     = ConfigFile.ReadByte("PASSWORD_PRIVATE_KEY"); 

    public static byte Generate(byte passwordPublicKey); 
} 

In meinem Unit-Test, ich kenne die Werte Die Password.Generate() Methode sollte für gegeben PASSWORD_PRIVATE_KEY und PASSWORD_PUBLIC_KEY zurückgeben. Aber ich möchte den PASSWORD_PRIVATE_KEY Wert verwendet in der Unit-Test-Klasse definiert werden, nicht in der Konfigurationsdatei:

[TestMethod] 
public void PasswordGenerate_CalculatedProperly() 
{ 
    byte passwordPublicKey = 0x22; 
    Password_Accessor.PASSWORD_PRIVATE_KEY = 0xF0; 
    byte expectedGenerated = 0xAA; 

    byte generated = Password_Accessor.Generate(passwordPublicKey); 

    Assert.AreEqual(expectedGenerated, generated); 
} 

Gibt es eine Weise, die ich die private static readonly durch Code schreiben kann, so dass ich nicht haben Verlasse ich irgendeine Konfigurationsdatei für meine Tests?

+0

Beachten Sie, dass die Frage anders lautet: http://StackOverflow.com/q/6851816/1192381 –

+0

Die kanonische Antwort lautet: "Verwenden Sie kein statisch initialisiertes' private static'-Feld "- refaktorieren Sie die' Password'-Klasse also erwirbt er den Wert für 'PASSWORD_PRIVATE_KEY' aus einer Collaborator-Klasse, die Sie dann nachahmen können.In diesem Fall scheint es, als wäre 'ConfigFile' nicht statisch die Antwort. – millimoose

+2

Im Allgemeinen ist "statisch" der eingeschworene Feind des Unit-Tests. – millimoose

Antwort

6

Um es sauber zu machen, müssen Sie Password mehr testbar machen. Um dies zu tun, sollten Sie dieses Design:

internal static class Password 
{ 
    public static void Configure(IPrivateKeyProvider keyProvider) 
    { 
     keyProvider = keyProvider; 
    } 

    public static byte Generate(byte passwordPublicKey); // use keyProvider 

    private static IPrivateKeyProvider* keyProvider; 
} 

internal interface IPrivateKeyProvider 
{ 
    byte GetPrivateKey(); 
} 

internal class ConfigPrivateKeyProvider : IPrivateKeyProvider 
{ 
    private static readonly byte PASSWORD_PRIVATE_KEY 
     = ConfigFile.ReadByte("PASSWORD_PRIVATE_KEY"); 

    public byte GetPrivateKey() 
    { 
     return PASSWORD_PRIVATE_KEY; 
    } 
} 

internal class PrivateKeyProviderStub : IPrivateKeyProvider 
{ 
    public PrivateKeyProviderStub(byte privateKey) 
    { 
     this.privateKey = privateKey; 
    } 

    public byte GetPrivateKey() 
    { 
     return this.privateKey; 
    } 
} 

jetzt Ihr Produktionscode ConfigPrivateKeyProvider verwenden kann und Tests können die PrivateKeyProviderStub verwenden.

Es ist ein wenig vereinfacht, Password als eine statische Klasse beizubehalten. Ich würde empfehlen, dies auch in eine gewöhnliche Klasse umzuwandeln, ein Singleton vielleicht, wenn es angemessen ist.

Beachten Sie auch, dass es viele Test-Frameworks gibt, die es erlauben, Mocks und Stubs im Handumdrehen zu erzeugen (Rhino Mocks zum Beispiel), so dass es nicht notwendig wäre, PrivateKeyProviderStub manuell zu implementieren.

+0

Vielen Dank für Ihre Antwort. Ich werde diesen Ansatz verwenden, aber stattdessen einen Mock für die 'ConfigFile'-Klasse verwenden, sodass ich die' Password'-Klasse für die Tests nicht ändern muss. –

1

Nr. Es gibt keine Möglichkeit, auf private Felder zuzugreifen.

Für Unit-Tests möchten Sie in der Regel auf interne Sachen zugreifen und Sie können das mit InternalsVisibleTo tun. Aber auch damit bleiben private Mitglieder privat. Vielleicht möchten Sie einen Getter für diese Felder definieren.

Edit: Mit ein bisschen umständlich Syntax können Sie auf private Felder mit PrivateObjects zugreifen.

+0

Verwenden von ' _Accessor'-Wrappern Ich habe Schreibrechte für private Felder in internen Klassen. Und das ist getan, die Anwendung in einem Projekt zu testen, und die Unit Klassen in einem anderen Projekt zu testen. Also, ich glaube nicht, das Problem ist die Tatsache, dass die Felder "private" sind, aber die Tatsache, dass sie "statisch readonly" sind und bereits einen Wert gegeben haben, wenn ich sie teste. –

2

Ich bin kein .NET Experte, nehmen Sie das mit einem Körnchen Salz.

Einer der Werte von Komponententests zeigt Ihnen, dass Sie Ihren Code überdenken müssen. Wenn es schwer zu testen ist, muss es neu geschrieben werden.

Finden Sie eine Möglichkeit, eine Abstraktion für Ihre Datei in Password zu injizieren, und erstellen Sie dann eine Scheinimplementierung dieser Abstraktion (z. B. mit Moq oder Rhino Mocks).

Aber wie Sie es jetzt haben, glaube ich nicht, es gibt einen Weg um eine Datei zu verwenden. Sie müssten Ihrem Test nur sagen, dass er sich stattdessen eine andere Datei ansehen soll.

Hoffe, dass hilft.

+0

"Sie müssten Ihrem Test nur sagen, dass er sich stattdessen eine andere Datei anschaut." Vielen Dank. Das werde ich tun. Einen Mock für 'ConfigFile' haben, der eigentlich nicht aus einer Konfigurationsdatei liest, sondern die Werte, die ich programmatisch zuordnen kann, bevor die' Password'-Klasse 'ReadByte()' aufruft. –

Verwandte Themen