Mockování ConfigurationSection (nebo celého configu)

Při unit-testování se někdy ocitnete v situaci, kdy potřebujete namockovat ConfigurationSection, resp. svého potomka. Konfigurace jsou v .NET ale celkem zapouzdřené a není úplně jednoduché překonat jejich výchozí read-only pojetí.

Nejlepší by samozřejmě bylo se od frameworkovin abstrahovat a na konfigurační data přistupovat přes nějaký adaptér, někdy ale prostě nemůžete být 100% pure (třeba musíte ConfigSection někomu dál předat), a přesto chcete nějaké pokrytí testy.

Existuje obskurní, nicméně funkční, cest, jak ConfigurationSection namockovat (případně celý konfigurační soubor, pokud je potřeba):

1. Vytvořte fake konfigurační soubor(y) ve svém testovacím projektu

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
<section name="mySection" type="MyConfigurationSection, MyAssembly" />
	</configSections>
	<mySection>
		<identities>
			<identity clientId="fake_id" name="FAKE_NAME" partitionName="FAKE_PARTITION" permissions="Full" />
		</identities>
	</mySection>
</configuration>

Soubor může být v jakékoliv složce, nejspíš přímo vedle třídy unit-testů.

2. Nastavte, aby se .config kopíroval do výstupu buildu

V Properties souboru nastavte:

  • Build Action = Content
  • Copy to Output Directory = Copy always

3. Vytvořte unit-test, který tento .config souboru použije pro vytoužený fake

[TestClass]
public class MyServiceTests
{
	public TestContext TestContext { get; set; }

	[TestMethod]
	[DeploymentItem("PathInTestProject\\MyServiceTests_ScenarioName.config")]
	public void MyService_DoSomething_ScenarioName_ExpectedBehavior()
	{
		// arrange
		ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap()
		{
			ExeConfigFilename = Path.Combine(TestContext.DeploymentDirectory, "MyServiceTests_ScenarioName.config")
		};
		Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
		MyConfiguarionSection mySection = config.GetSection("mySection") as MyConfiguarionSection ;

		var sut = new MyService(mySection);

		// act
		...
	}
}

V čem to spočívá:

  • Metoda ConfigurationManager.OpenMappedExeConfiguration() vás nechá načíst konfiguraci z libovolného souboru.
  • Atribut [DeploymentItem(...)] zařídí zkopírování souboru z výstupní složky buildu do „test deployment directory“ (např. $(SolutionDir)\TestResults\Deploy_username 2017-09-29 22_02_39\Out\)
  • Přes TestContext.DeploymentDirectory se dostanete na aktuální cestu složky, přičemž do TestContext property se vám automaticky nainjectuje testovací kontext.

Azure: Ověření existence blobu, pokud mám jeho URI

Pokud máte pouze storage account (CloudBlobClient) a URI blobu v něm, není úplně jednoduché existenci blobu ověřit. Přesněji řečeno při touze použít obvyklou metodu ​​ICloudBlob.Exists()​, musíte mít na daný blob referenci. Pro její získání z URI je tu metoda CloudBlobClient.GetBlobReferenceFromServer(Uri blobUri), která však při svém zavolání dělá HEAD request na blob a snaží se dotáhnout jeho metadata. Pokud blob neexistuje, vyhodí už tato metoda výjimku a na nějaké Exists() se už ani nedostane.

Výjimku lze zachytit s pěkným pattern-matchingem:

ICloudBlob blob;
try
{
    blob = blobClient.GetBlobReferenceFromServer(new Uri(url));
}
catch (Microsoft.WindowsAzure.Storage.StorageException ex)
            when ((ex.InnerException is System.Net.WebException wex)
                    && (wex.Response is System.Net.HttpWebResponse httpWebResponse)
                    && (httpWebResponse.StatusCode == System.Net.HttpStatusCode.NotFound))
{
    // blob does not exist, do whatever you need here
    return null;
}
// further code able to use the blob-reference

Pokud s referencí na blob nepotřebujete dále pracovat, tak samozřejmě můžete jeho existenci ověřit vlastním jednoduchým HTTP HEAD requestem z libovolného HTTP klienta.
Každopádně je zde obskurní už to, že máte na daný blob pouze plné URL. Za příčetných okolností byste měli mít k dispozici strukturovanou informaci o názvu containeru a blobu v něm, což by šlo samozřejmě z URL i vyparsovat.

Visual Studio: Klávesové zkratky pro navigaci na další/předchozí metodu v kódu

V Resharperu jsou známé klávesové zkratky pro navigaci na dalšího (Alt+Down) resp. předchozího (Alt+Up) membera v kódu. Málo se ale ví, že i čisté Visual Studio toto nyní umí.

Ve Visual Studiu (minimálně ve verzi 2017, ale nejspíš to umí i starší verze) můžete tyto klávesové zkratky získat namapováním příkazů Edit.NextMethodEdit.PreviousMethod (přestože mluvíme o metodách, ve skutečnosti se skáče po memberech). Tyto commandy bývaly funkční pouze pro Visual Basic, jak se teď ale vývoj pro všechny jazyky sjednocuje, fungují i v C#:

2017-09-26_10-12-19

…já asi zvolím klávesové zkratky Ctrl+UpCtrl+Down, protože mi to nějak lépe jde pod ruce a to výchozí skrolování, co tam je, je stejně k ničemu. Mimochodem Alt+Up/Down používám pro posun řádku/selection o řádek výš/níž.

Azure App Service: Získání full memory dumpu při výskytu určité výjimky

Opakovaně si libuji, co všechno je na Azure App Service z hlediska diagnostiky a správy možné ve srovnání s tradičním webhostingem. Například získat full memory dump je snadné, díky Kudu a SysInternals, které jsou na AppService k dispozici.

…no a pokud ladím konkrétní problém a potřebuji memory dump v momentu výskytu určité výjimky, je to už jenom o správném použití ProcDump ze SysInternals (spustit z Debug Console, kde si aktuální adresář přepnete třeba na LogFiles:


d:\devtools\sysinternals\procdump -accepteula -ma -e 1 -f "FaultException" 9736

…poslední číslo je číslo procesu (získáte z Kudu z Process Exploreru nebo z portálu), v uvozovkách stačí libovolná část názvu typu výjimky (testuje se přes Contains).

Celá věc pak může vypadat třeba takhle:


Kudu Remote Execution Console
Type 'exit' then hit 'enter' to get a new CMD process.
Type 'cls' to clear the console

Microsoft Windows [Version 6.2.9200]
(c) 2012 Microsoft Corporation. All rights reserved.

D:\home>
D:\home\LogFiles>d:\devtools\sysinternals\procdump -accepteula -ma -e 1 -f "FaultException" 9736

ProcDump v9.0 - Sysinternals process dump utility
Copyright (C) 2009-2017 Mark Russinovich and Andrew Richards
Sysinternals - www.sysinternals.com

Process: w3wp.exe (9736)
Process image: D:\Windows\SysWOW64\inetsrv\w3wp.exe
CPU threshold: n/a
Performance counter: n/a
Commit threshold: n/a
Threshold seconds: n/a
Hung window check: Disabled
Log debug strings: Disabled
Exception monitor: First Chance+Unhandled
Exception filter: [Includes]
*FaultException*
[Excludes]
Terminate monitor: Disabled
Cloning type: Disabled
Concurrent limit: n/a
Avoid outage: n/a
Number of dumps: 1
Dump folder: D:\home\LogFiles\
Dump filename/mask: PROCESSNAME_YYMMDD_HHMMSS
Queue to WER: Disabled
Kill after dump: Disabled

Press Ctrl-C to end monitoring without terminating the process.

CLR Version: v4.0.30319

[07:57:32] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:57:40] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:57:50] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:57:55] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:02] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:10] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:20] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:24] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:32] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:40] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:50] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:58:55] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:02] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:10] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:20] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:25] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:32] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:40] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:50] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:55] Exception: E0434F4D.System.Threading.ThreadAbortException ("Thread was being aborted.")
[07:59:59] Exception: E0434F4D.System.ServiceModel.FaultException ("The message with Action '' cannot be processed at the receive
r, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Acti
ons between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and recei
ver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).")
[07:59:59] Dump 1 initiated: D:\home\LogFiles\w3wp.exe_170829_075959.dmp
[08:00:04] Dump 1 writing: Estimated dump file size is 290 MB.
[08:00:11] Dump 1 complete: 290 MB written in 12.1 seconds
[08:00:12] Dump count reached.

D:\home\LogFiles>

WCF: The message with Action “ cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher.

Pokud voláte WCF webovou službu (SOAP) a dostáváte tuto response

The message with Action “ cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

…může to být kromě obvyklých zřejmých důvodů způsobeno i zcela chybějící HTTP-hlavičkou SOAPAction v příchozím HTTP-POST SOAP requestu (match je potom možný pouze na *-wildcard-action, cokoliv jiného končí chybou výše).

ASP.NET Core: Duplicate ‚Content‘ items were included. The .NET SDK includes ‚Content‘ items from your project directory by default.

Pokud dostanete na ASP.NET Core projektu ve Visual Studiu hlášku o duplicitním Contentu:

Error Duplicate ‚Content‘ items were included. The .NET SDK includes ‚Content‘ items from your project directory by default. You can either remove these items from your project file, or set the ‚EnableDefaultContentItems‘ property to ‚false‘ if you want to explicitly include them in your project file. For more information, see https://aka.ms/sdkimplicititems. The duplicate items were: ‚wwwroot\Index.html‘ Web C:\Program Files\dotnet\sdk\1.1.0\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.DefaultItems.targets 188

…jedná se o nadrbaný .csproj a pomůže odebrat a přidat složky wwwroot z projektu:

  1. Solution Explorer – pravým na ~/wwwroot – Exclude From Project
  2. zapnout si viditelnost excludovaných souborů – Solution Explorer – Show All Files (jedna z ikonek nahoře)
  3. Solution Explorer – pravým na ~/wwwroot – Include in Project

TFS2017: SQL Query pro počet changesetů dle uživatele

…byl to nakonec trochu opruz dohledat správně uživatele, tak dávám do pléna

SELECT i.AccountName, COUNT(*)
	FROM tbl_ChangeSet AS cs
		LEFT JOIN tbl_IdentityMap im ON cs.OwnerId = im.localId
		LEFT JOIN Tfs_Configuration.dbo.tbl_Identity i ON im.masterId = i.Id
	GROUP BY i.AccountName WITH ROLLUP
	ORDER BY COUNT(*) DESC

Tipy a triky v .NET a C# – slides, dema a záznam [TechEd Praha 05/2017]

Slides, dema a záznam z mé přednášky pro TechEd DevCon Praha ze 17.5.2017:

Záznam z přednášky je publikován na našem HAVIT YouTube Channel.

Dotčená témata

  • VS – Windows Layouts
  • C# – NoRefactoringsInInactiveCode
  • C# – MSTestV2TestCases
  • VS Keyboard Shortcuts
  • C# – InternalsVisibleTo
  • C# – Test – HardcodedDependenciesTestability
  • VS – [Re-]Attach to process
  • VS – BoxSelection
  • C# – NameOf – nameof()
  • C# – Initializers + InitializersArray
  • VS – MoveLineUpDown
  • C# 5.0 – Caller Info Attributes
  • VS – Extensions.md
  • VS – Personal Tweaks.md
  • C# – Using Without Variable
  • C# – DebuggerDisplay ([DebuggerBrowsable], [DebuggerStepThrough])
  • C# – AsyncLocal

Azure PaaS – Praktické zkušenosti, tipy a triky – slides a záznam [TechEd Praha 05/2017]

Slides, dema a záznam z mé přednášky pro TechEd DevCon Praha z 16.5.2017:

Záznam z přednášky je publikován na našem HAVIT YouTube Channel.

Dotčená témata

  • IaaS vs. PaaS vs. XaaS
  • Motivace k Azure PaaS vs. Co vás může odradit
  • Klíčové služby
    • App Service
    • Azure SQL
    • Azure Storage
    • Application Insights
  • Praktické scénáře
    • Deployment (MSDeploy)
    • Práce se soubory IFileStorage
    • Práce s časem (DateTime.Now v UTC vs. ITimeService)
    • Posílání e-mailů (SendGrid)
    • Naplánované úlohy (Quartz.NET, WebJobs, WebJobs SDK, Dashboard)

Novinky ve VS2017 a C# 7.0 – slides, dema a záznam [TechEd Praha 05/2017]

Slides, dema a záznam z mé přednášky pro TechEd DevCon Praha z 15.5.2017:

Záznam z přednášky je publikován na našem HAVIT YouTube Channel.

Dotčená témata

  • VS 2017 Novinky
    • Go to All… (Naviagate to…)
    • IntelliSense
    • Find All References
    • Structure Guides
    • Live Unit Testing
    • Installer
    • Debugging – Run to Click
    • Debugging – Exception Helper (NullReferenceException)
  • MSTest v2 [DataRow]
  • Novinky C# 7.0
    • Binary Literals
    • Digit Separators
    • Expression Bodied Members – Constructors, Destructors (Finalizers), Property Accessors
    • throw expressions
    • out variables
    • Tuples
    • Local Functions
    • Pattern Matching
    • ValueTask<>
    • Ref Return, Ref Local