Interesanta lieta, kas varbūt nav tik bieži un daudz apskatīta lasot par jaunās versijas iespējām un jaunajām “fīčām” – Fakes Framework.
Šī platforma paredzēta tieši vienumu testu efektīvākai un vieglākai rakstīšanai.
Dažkārt vienumu testu veidošanas laikā ir nepieciešams simulēt kādas izmantotas komponentes uzvedību vai funkcionalitāti. Ja programma, kuru vēlas notestēt un pārējās komponentes ir veidotas ar ņemot vērā vienumu testu īpatnības (piemēram, komponentes paļaujas un izmanto funkcionalitāti, kas slēpjas aiz klašu interfeisiem, kurus “saņem” no ārpuses), tad lielas grūtības pareizu vienumu testu uzrakstīt parasti nesagādā problēmas.
Problēmas parasti rodas situācijās, kad nepieciešams simulēt uzvedību kādai no komponentēm, kurai nav pieejams izejas kods, kurai nav iespējams pārrakstīt uzvedību (virtuālās metodes) vai arī kuras paļaujas uz kādu konkrētu iekodētu implementāciju, kuru izmainīt parasti sagādā problēmas.
Pieņemsim, ir neliela metode, kas pārbauda entity “derīguma termiņu” un nosaka, vai šo entity var dzēst no sistēmas. Pieņemsim, ka entity no sistēma var tikai tad, kad tā tur pabijusi vairāk par 5 dienām, t.i., entity pievienošanas datums ir par 5 dienām mazāks nekā {sistēmas pašreizējais} datums. Un šajā vietā sākas interesantākais. {Pašreizējais datums} ir lieta, kuru parasti implementē ar DateTime.Now.
Viss ir kārtībā un sistēma visticamāk arī uzvedīsies pareizi run-time laikā, bet jautājums: kā, lai pārbauda šāda veida kodu no vienumu testiem. Un vienumu testi noteikti gribēs pārbaudīt abus loģiskos zarus – kad entity pievienošanas laiks ir mazāks un kad ir lielāks par definētajām 5 dienām, tādā veidā tiešām pārliecinoties, ka abi loģikas zari nostrādā pareizi.
public bool CanEntityRemove(Entity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
var delta = entity.Added.Subtract(DateTime.Now);
return delta.Days > 5;
}
Viens no variantiem, būtu definēt pašiem savu datuma piegādātāja klasi, kas varētu atgriezt virtuālo laiku, ar kuru varētu veikt nepieciešamās manipulācijas pirms atgriešanas. Bet jautājums, ko darīt, ja vēlamies pārbaudīt *tieši* šādu kodu? Talkā nāk Fakes Framework, kas pieejams Visual Studio Dev ’11 versijā.
Lai aizvietotu DateTime.Now īpašības vērtības, nepieciešams vispirms vienumu testa projektam uz nepieciešamo references bibliotēku, kur definēta klase DateTime, izpildīt komandu “Add Fakes Assembly”.

Šī komanda ģenerēs jaunu bibliotēku {OriginalName} + “.Fakes”. Par cik DateTime ir definēts mscorlib bibliotēkā, bet pēc noklusējuma tā nav redzama references sarakstā, tad pēc Fakes bibliotēkas ģenerācijas, pieejama ir mscorlib.4.0.0.0.Fakes bibliotēka, kas satur stubs vai shims objektu veidus.

Microsoft Visual Studio Dev ‘11 pašlaik izmanto divu klašu “izbāžņu” veidus:
- Stub: ir veids, kurā ģenerētā klase pilnībā ir spējīga aizvietot mērķa klasi, piedāvājot noklusēto uzvedību un funkcionalitāti visām virtuālajām vai abstraktajām metodēm, īpašībām un notikumiem. Šīs klašu “izkārtnes” iespējams ģenerēt arī interfeisiem un nemarķētām (non-sealed) klasēm. Savukārt pārējiem gadījumiem: statiskām vai ne-virtuālām metodēm jāģenerē būs Shim veida aizbāžņi.
- Shim: ar šī veida palīdzību ir iespējams simulēt uzvedību statiskām vai ne-virutālām metodēm. Šī veida “izbāžņi” pārķers visus orģinālos izsaukumus un pāradresēs to alternatīvajai implementācijai.
Abu šo veidu klašu “izbāžņi” ļauj izmantot delegates, lai implementētu vai pārrakstītu nepieciešamo funkcionalitāti.
Direktorija “Fakes/” satur sarakstu un papildus informāciju par bibliotēkām, kurām ir jāģenerē klašu “izbāžņi”.
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
<Assembly Name="mscorlib" Version="4.0.0.0"/>
</Fakes>
Ja gribam simulēt DateTime.Now īpašības vērtību, tad nozīmē, ka jāizmanto būs Shim veida ģenerācija, jo īpašība ir statiska.
Lai darbotos Shim veida ģenerētās klases, vispirms nepieciešams ieslēgt Shim kontekstu vienuma testa izpildes laikā. Shim konteksts paļaujas uz speciāli veidotu TraceProfilerInstrumentationProvider, kas arī nodarbojas ar izsaukumu pārķeršanu.
Tātad, ja vēlamies izmantot Shim veida klašu ģenerāciju, tad no sākuma jāieslēdz:
[TestMethod]
public void CanEntityAddedOnJanuaryRemoved_Test()
{
using (ShimsContext.Create())
{
Ja aizmirsīsim kontekstu ievietot using blokā, tad simulācija būs spēkā visa AppDomain dzīves laikā. Tāpēc svarīgi ir atcerēties kontrolēt Shim konteksta scope. Pēc tam, kad Shim konteksts ir uzstādīts, ir nepieciešams nodot instrukcijas klasei, kura jāsimulē.
Tas notiek ar lambda izteiksmes palīdzību:
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () => new DateTime(2012, 1, 1);
Šajā gadījumā tiek simulēts DateTime.Now īpašības uzvedība un atgrieztā vērtība.
Izpildot vienuma testu redzams, ka Shim konteksts strādā un patiešām DateTime.Now property atgriež nepieciešamo vērtību.

Ja nepieciešams, piemēram, aizvietot vispār EntityValidator loģiku un testēt pavisam kādu citu sistēmas apgabalu pieņemot , ka vienumu testā šai klasei vienmēr ir jāatgriež true vērtība?
Tas nozīmē, ka iespējami ir 2 scenāriji:
- Izmantojam Shims ģenerācijas veidu un pielietojam jau apskatīto tehnoloģiju, kā modificēt vērtības klasēm, kurām nespējam piekļūt vienkāršā veidā.
- Pārdefinējam metodi CanEntityRemove() par virtuālu un izmantojam Stub klašu “izbāzeņu” ģenerācijas veidu, lai simulētu, ka dzēst no sistēmas var entity ar jebkādu datumu.
Lai šāda veida uzvedību nosimulētu, nepieciešams atkal Stub ģenerētajai klasei noinstruēt, ka vienmēr neskatoties uz kādu entity pievienošanas datumu, jāatgriež vienmēr true.
[TestMethod]
public void CanEntityRemove_StubTest()
{
var stub = new StubEntityValidator();
stub.CanEntityRemoveEntity = (entity => true);
var result = stub.CanEntityRemove(new Entity {Added = new DateTime(2012, 12, 12)});
Assert.IsTrue(result);
}
Šāda veida Stub ģenerētu uz noinstruēt klases instance var padot tālāk jau citām komponentēm, kurām nepieciešama šis objekts.
Cerams, ka noderēs!