Stosowanie wycinków kodu do izolowania od siebie poszczególnych części aplikacji w celu przeprowadzania testów jednostkowych
Stub typy są jednym z dwóch technologii, dostępnych w ramach Fakes firmy Microsoft pozwalają łatwo izolować testowany z innych składników, które wywołuje składnik.Pseudoprocedura jest kawałek kodu, która ma miejsce innego komponentu podczas badania.Korzyści wynikające z zastosowania w wersji uproszczonej jest, że zwraca ona spójne wyniki, co ułatwia pisanie badania.I można uruchomić testy, nawet jeśli inne składniki nie są jeszcze działa.
Omówienie i szybki start do podrobionych, zobacz Izolowanie testowanego kodu za pomocą struktury Microsoft Fakes.
Aby korzystać z procedur wejścia, trzeba napisać składnik, tak aby uzywalo tylko interfejsy, nie z klas, aby odwołać się do innych części aplikacji.Jest to praktyka dobrym projektem, ponieważ to sprawia, że zmiany w jednej części mniej prawdopodobne, aby wymagać zmian w innym.Do testowania, pozwala zastąpić skrótowej na prawdziwe składnika.
Na diagramie składnik StockAnalyzer jest ten, który chcemy sprawdzić.Zwykle używa innego komponentu, RealStockFeed.Ale RealStockFeed zwraca różne wyniki przy każdym jego metody są wywoływane, co utrudnia do testowania StockAnalyzer.Podczas badania, możemy ją zastąpić innej klasy StubStockFeed.
Ponieważ procedur wejścia opierają się na swoje możliwości do struktury kodu w ten sposób, zazwyczaj używa procedur wejścia do izolowania jednej strony z innej aplikacji.Odłączające je od innych zespołów, które są nie masz kontroli, takich jak System.dll, których zazwyczaj jest używane podkładki.Zobacz Stosowanie podkładek do izolowania aplikacji od innych zestawów w celu przeprowadzania testów jednostkowych.
Wymagania
- Visual Studio Ultimate
W tym temacie
Jak używać procedur wejścia
Projekt dla współzależności iniekcji
Generowanie procedur wejścia
Napisać taki Test za pomocą procedur wejścia
Weryfikowanie wartości parametrów
Procedur wejścia dla różnych rodzajów elementy członkowskie typu
Jak używać procedur wejścia
Projekt dla współzależności iniekcji
Aby korzystać z procedur wejścia, aplikacja musi być tak zaprojektowane, aby poszczególne składniki nie są od siebie niezależne, ale tylko zależne od definicji interfejsów.Zamiast jest sprzężona w czasie kompilacji, składniki są połączone w czasie wykonywania.Ten wzór pomaga, aby oprogramowanie, które jest niezawodne i łatwe do aktualizowania, ponieważ zmiany zwykle nie propagować ponad granicami składnika.Zaleca się po nim, nawet jeśli nie używasz procedur wejścia.Jeśli piszesz nowy kod jest łatwiejszy w odbiorze zależność iniekcji wzór.Jeśli piszesz testy dla istniejącego oprogramowania należy refaktoringu go.Jeśli to byłoby niepraktyczne, można rozważyć zamiast podkładki.
Zacznijmy od tej dyskusji motywujące przykład, jeden na diagramie.Klasy, którą odczytuje StockAnalyzer udostępniać ceny i generuje interesujące wyniki.Ma ona niektóre metody publiczne, które chcemy sprawdzić.Aby zachować rzeczy proste, po prostu Spójrzmy na jednej z tych metod, jeden bardzo proste, które przedstawia aktualną cenę konkretną akcją.Chcemy, aby napisać test jednostki tej metody.Oto pierwszy projekt testu:
[TestMethod]
public void TestMethod1()
{
// Arrange:
var analyzer = new StockAnalyzer();
// Act:
var result = analyzer.GetContosoPrice();
// Assert:
Assert.AreEqual(123, result); // Why 123?
}
<TestMethod()> Public Sub TestMethod1()
' Arrange:
Dim analyzer = New StockAnalyzer()
' Act:
Dim result = analyzer.GetContosoPrice()
' Assert:
Assert.AreEqual(123, result) ' Why 123?
End Sub
Jednym z problemów z tego testu jest oczywiste: różnią się cen akcji, a więc zazwyczaj nie twierdzenie.
Inny problem może być, że składnik StockFeed, który jest używany przez StockAnalyzer, jest wciąż w fazie rozwoju.Oto pierwszy projekt kod metody badanego:
public int GetContosoPrice()
{
var stockFeed = new StockFeed(); // NOT RECOMMENDED
return stockFeed.GetSharePrice("COOO");
}
Public Function GetContosoPrice()
Dim stockFeed = New StockFeed() ' NOT RECOMMENDED
Return stockFeed.GetSharePrice("COOO")
End Function
W obecnej, ta metoda nie może skompilować lub może zgłosić wyjątek, ponieważ prace nad klasy StockFeed nie została jeszcze zakończona.
Wtrysk interfejsu rozwiązuje oba te problemy.
Interfejs iniekcji stosuje się następującą regułę:
Kod wybranego komponentu aplikacji nigdy nie jawnie powinno odnosić się do klasy w innym składniku, w deklaracji lub w new instrukcji.Zamiast tego zmiennych i parametrów powinny zostać uznane za z interfejsami.Wystąpienia składników powinny być tworzone tylko przez kontener składnika.
"Składnik" w tym przypadku oznacza klasy lub grupy klas, które można rozwijać i uaktualniać ze sobą.Zazwyczaj składnikiem jest kod w jednym projekcie programu Visual Studio.Może to być mniej ważne, aby oddzielić klas w obrębie jednego składnika, ponieważ są one aktualizowane w tym samym czasie.
Również nie jest tak ważne, aby oddzielić składniki od klas stosunkowo stabilna platforma, takich jak System.dll.Pisanie interfejsów dla tych klas będzie zaśmiecać kodu.
Kod StockAnalyzer dlatego można poprawić poprzez oddzielenie od StockFeed, przy użyciu interfejsu, jak ten:
public interface IStockFeed
{
int GetSharePrice(string company);
}
public class StockAnalyzer
{
private IStockFeed stockFeed;
public Analyzer(IStockFeed feed)
{
stockFeed = feed;
}
public int GetContosoPrice()
{
return stockFeed.GetSharePrice("COOO");
}
}
Public Interface IStockFeed
Function GetSharePrice(company As String) As Integer
End Interface
Public Class StockAnalyzer
' StockAnalyzer can be connected to any IStockFeed:
Private stockFeed As IStockFeed
Public Sub New(feed As IStockFeed)
stockFeed = feed
End Sub
Public Function GetContosoPrice()
Return stockFeed.GetSharePrice("COOO")
End Function
End Class
W tym przykładzie StockAnalyzer jest przekazywana z implementacji IStockFeed, gdy jest skonstruowany.Wypełniony wniosek kod inicjujący będzie wykonywać połączenia:
analyzer = new StockAnalyzer(new StockFeed())
Istnieją bardziej elastyczne sposoby wykonywania tego połączenia.Na przykład StockAnalyzer mogła przyjąć obiekt fabryki, można utworzyć wystąpienia różne implementacje IStockFeed w różnych warunkach.
Generowanie procedur wejścia
Już oddzielonej klasę, którą chcesz przetestować z innymi składnikami, których używa.Jak również zwiększenia stosowania solidna i elastyczna, oddzielenie pozwala połączyć składnik badany stub implementacji interfejsów dla celów testowych.
Po prostu napisać procedury wejścia jako klasy w zwykły sposób.Ale Fakes Microsoft zapewnia bardziej dynamiczny sposób stworzyć najbardziej odpowiednim stub dla każdego badania.
Aby korzystać z procedur wejścia, należy najpierw wygenerować typy skrótowej z definicji interfejsów.
Dodawanie zestawu podrobionych
W oknie Solution Explorer rozwiń projekt testu jednostki odwołania.
- Jeśli pracujesz w języku Visual Basic, należy wybrać Pokaż wszystkie pliki na pasku narzędziowym panelu Solution Explorer, aby wyświetlić listę odwołania.
Wybierz zestaw, który zawiera definicje interfejsu, dla których chcesz utworzyć procedur wejścia.
W menu skrótów wybierz polecenie Dodać sfałszować zestawu.
Napisać taki test za pomocą procedur wejścia
[TestClass]
class TestStockAnalyzer
{
[TestMethod]
public void TestContosoStockPrice()
{
// Arrange:
// Create the fake stockFeed:
IStockFeed stockFeed =
new StockAnalysis.Fakes.StubIStockFeed() // Generated by Fakes.
{
// Define each method:
// Name is original name + parameter types:
GetSharePriceString = (company) => { return 1234; }
};
// In the completed application, stockFeed would be a real one:
var componentUnderTest = new StockAnalyzer(stockFeed);
// Act:
int actualValue = componentUnderTest.GetContosoPrice();
// Assert:
Assert.AreEqual(1234, actualValue);
}
...
}
<TestClass()> _
Class TestStockAnalyzer
<TestMethod()> _
Public Sub TestContosoStockPrice()
' Arrange:
' Create the fake stockFeed:
Dim stockFeed As New StockAnalysis.Fakes.StubIStockFeed
With stockFeed
.GetSharePriceString = Function(company)
Return 1234
End Function
End With
' In the completed application, stockFeed would be a real one:
Dim componentUnderTest As New StockAnalyzer(stockFeed)
' Act:
Dim actualValue As Integer = componentUnderTest.GetContosoPrice
' Assert:
Assert.AreEqual(1234, actualValue)
End Sub
End Class
Specjalne kawałek Magiczna w tym miejscu jest klasa StubIStockFeed.Dla każdego typu publicznych w Zgromadzeniu do którego istnieje odwołanie mechanizm Fakes Microsoft generuje Klasa zastępcza.Nazwa klasy skrótowej jest pochodna od nazwy interfejsu, z "Fakes.Stub" jako prefiks i nazwy typu parametrów, które są dołączone.
Są również wygenerowany dla pobierające i ustawiające metody, właściwości, zdarzenia i metody rodzajowe.
Weryfikowanie wartości parametrów
Można zweryfikować, że gdy składnik dzwoni do innego składnika, przechodzi poprawne wartości.Można umieścić możliwość skorzystania w procedura wejścia lub można przechowywać wartość i sprawdź go w głównej części badania.Na przykład:
[TestClass]
class TestMyComponent
{
[TestMethod]
public void TestVariableContosoPrice()
{
// Arrange:
int priceToReturn;
string companyCodeUsed;
var componentUnderTest = new StockAnalyzer(new StubIStockFeed()
{
GetSharePriceString = (company) =>
{
// Store the parameter value:
companyCodeUsed = company;
// Return the value prescribed by this test:
return priceToReturn;
};
};
// Set the value that will be returned by the stub:
priceToReturn = 345;
// Act:
int actualResult = componentUnderTest.GetContosoPrice(priceToReturn);
// Assert:
// Verify the correct result in the usual way:
Assert.AreEqual(priceToReturn, actualResult);
// Verify that the component made the correct call:
Assert.AreEqual("COOO", companyCodeUsed);
}
...}
<TestClass()> _
Class TestMyComponent
<TestMethod()> _
Public Sub TestVariableContosoPrice()
' Arrange:
Dim priceToReturn As Integer
Dim companyCodeUsed As String = ""
Dim stockFeed As New StockAnalysis.Fakes.StubIStockFeed()
With stockFeed
' Implement the interface's method:
.GetSharePriceString = _
Function(company)
' Store the parameter value:
companyCodeUsed = company
' Return a fixed result:
Return priceToReturn
End Function
End With
' Create an object to test:
Dim componentUnderTest As New StockAnalyzer(stockFeed)
' Set the value that will be returned by the stub:
priceToReturn = 345
' Act:
Dim actualResult As Integer = componentUnderTest.GetContosoPrice()
' Assert:
' Verify the correct result in the usual way:
Assert.AreEqual(priceToReturn, actualResult)
' Verify that the component made the correct call:
Assert.AreEqual("COOO", companyCodeUsed)
End Sub
...
End Class
Procedur wejścia dla różnych rodzajów elementy członkowskie typu
Metody
Opisane w przykładzie, metod można stubbed dołączając pełnomocnika do instancji klasy skrótowych.Nazwa typu skrótowej pochodzi od nazwy metody i parametrów.Na przykład, biorąc pod uwagę następujący interfejs IMyInterface i metodę MyMethod:
// application under test
interface IMyInterface
{
int MyMethod(string value);
}
Dołączamy namiastkę do MyMethod, która zawsze zwraca 1:
// unit test code
var stub = new StubIMyInterface ();
stub.MyMethodString = (value) => 1;
Jeśli nie podasz stub dla funkcji, podrobionych wygeneruje funkcję zwracającą wartość domyślna typ zwracany.Dla liczb, wartością domyślną jest 0, a dla typu klasy jest null (C#) lub Nothing (Visual Basic).
Właściwości
Metody pobierając i ustawiające wartości właściwości są dostępne jako oddzielne delegaty i namiastki dla nich mogą być tworzone oddzielnie.Na przykład, rozważmy właściwość Value z IMyInterface:
// code under test
interface IMyInterface
{
int Value { get; set; }
}
Do symulowania automatycznej właściwości, można dołączyć delegaty do metod pobierających i ustawiających Value:
// unit test code
int i = 5;
var stub = new StubIMyInterface();
stub.ValueGet = () => i;
stub.ValueSet = (value) => i = value;
Jeśli nie podasz metody stub dla setter lub metody pobierającej właściwości, podrobionych wygeneruje skrótową, który przechowuje wartości, tak, aby właściwość skrótowej działa tak, jak proste zmiennej.
Zdarzenia
Zdarzenia są uwidocznione jako pola delegatów.W rezultacie wszystkie zdarzenia z utworzoną namiastką mogą być łatwo wywoływane poprzez wywołanie zdarzenia pola pomocniczego.Rozważmy następujący interfejs do tworzenia namiastki:
// code under test
interface IWithEvents
{
event EventHandler Changed;
}
Aby wywołać zdarzenie Changed, możemy po prostu wywołać pomocniczy delegat:
// unit test code
var withEvents = new StubIWithEvents();
// raising Changed
withEvents.ChangedEvent(withEvents, EventArgs.Empty);
Metody standardowe
Istnieje możliwość tworzenia namiastek dla metod generycznych, poprzez dostarczenie delegatu dla każdego pożądanego wystąpienia metody.Na przykład, biorąc pod uwagę następujący interfejs, zawierający metodę generyczną:
// code under test
interface IGenericMethod
{
T GetValue<T>();
}
można napisać test, który kluby piłkarskie GetValue<int> podczas tworzenia wystąpienia:
// unit test code
[TestMethod]
public void TestGetValue()
{
var stub = new StubIGenericMethod();
stub.GetValueOf1<int>(() => 5);
IGenericMethod target = stub;
Assert.AreEqual(5, target.GetValue<int>());
}
Jeśli kod zebraniu na GetValue<T> z innych instancji, procedura wejścia byłoby po prostu wywołać zachowanie.
Kategoria: wirtualnych klas
W poprzednich przykładach procedury wejścia zostały wygenerowane z interfejsów.Można również generować procedur wejścia, z klasy, w których znajdują się członkowie virtual lub abstract.Na przykład:
// Base class in application under test
public abstract class MyClass
{
public abstract void DoAbstract(string x);
public virtual int DoVirtual(int n)
{ return n + 42; }
public int DoConcrete()
{ return 1; }
}
W skrótowej generowane na podstawie tej klasy można ustawić metody delegowanego dla DoAbstract() i DoVirtual(), ale nie DoConcrete().
// unit test
var stub = new Fakes.MyClass();
stub.DoAbstractString = (x) => { Assert.IsTrue(x>0); };
stub.DoVirtualInt32 = (n) => 10 ;
Jeżeli pełnomocnik nie zostanie określona dla metody wirtualne, podrobionych albo może zapewnić zachowanie domyślne lub może wywołać metodę w klasie podstawowej.Aby mieć metody podstawowej, o nazwie, należy ustawić CallBase właściwość:
// unit test code
var stub = new Fakes.MyClass();
stub.CallBase = false;
// No delegate set – default delegate:
Assert.AreEqual(0, stub.DoVirtual(1));
stub.CallBase = true;
//No delegate set - calls the base:
Assert.AreEqual(43,stub.DoVirtual(1));
Namiastki debugujące
Typy namiastki zostały zaprojektowane, aby zapewniać płynność debugowania.Domyślnie, debuger pomija kod generowany, więc powinien wejść bezpośrednio do niestandardowych implementacji elementu członkowskiego, które zostały dołączone do namiastki.
Ograniczenia namiastki
Podpisy metod ze wskaźnikami nie są obsługiwane.
Zapieczętowane klasy lub metody statyczne nie mogą mieć utworzonej namiastki, ponieważ typy namiastki opierają się na wysyłaniu wirtualnej metody.W takich przypadkach należy używać typów podkładkowych, opisanych w Stosowanie podkładek do izolowania aplikacji od innych zestawów w celu przeprowadzania testów jednostkowych
Zmiana domyślnego zachowania namiastki
Każdy wygenerowany typ namiastki posiada wystąpienie interfejsu IStubBehavior (poprzez właściwość IStub.InstanceBehavior).Zachowanie jest wywoływane zawsze, gdy klient wywołuje element członkowski, który nie ma dołączonego niestandardowego delegata.Jeśli nie ustawiono zachowania, użyte zostanie wystąpienie zwrócone przez właściwość StubsBehaviors.Current.Domyślnie właściwość ta zwraca zachowanie, które zgłasza wyjątek NotImplementedException.
Zachowanie można zmienić w dowolnym momencie, przez ustawienie właściwości InstanceBehavior na dowolnym wystąpieniu namiastki.Na przykład, poniższa wstawka kodu zmienia zachowanie, które nie wykonuje działania lub zwraca domyślną wartość typu zwracanego: default(T):
// unit test code
var stub = new StubIFileSystem();
// return default(T) or do nothing
stub.InstanceBehavior = StubsBehaviors.DefaultValue;
Zachowanie można także zmienić globalnie, dla wszystkich obiektów z namiastką, których zachowanie nie zostało ustawione, poprzez ustawienie właściwości StubsBehaviors.Current właściwość:
// unit test code
//change default behavior for all stub instances
//where the behavior has not been set
StubBehaviors.Current =
BehavedBehaviors.DefaultValue;
Zasoby zewnętrzne
Wskazówki
Badania na nieprzerwane z Visual Studio 2012-rozdział 2: Testowanie jednostek: testowanie wewnątrz
Zobacz też
Koncepcje
Izolowanie testowanego kodu za pomocą struktury Microsoft Fakes