Results for category "C#"

10 Articles

Connecting ASP.NET MVC, MSBuild, CasperJS and TeamCity for user interface testing

I must admit I have been ducking of implementing a working integration test suite in our current project for a long, long time. Some days ago I had to react on this fact as we needed to test some code of the  JavaScript frontend which had to interact with the ASP.NET backend through AJAX requests. The last time I tried to implement integration tests, I used Selenium which was some kind of … ugly so I decided to give CasperJS a try.

You can find the complete example in my Github repository: https://github.com/schakko/asp.net-msbuild-casperjs-teamcity.

Fehlerhafte Unittests in NUnit und xUnit von TeamCity nacheinander ausführen lassen

Aus äh… historischen Gründen ist es so, dass eines unserer C#-Projekte sowohl NUnit als auch xUnit für die Unittests nutzt. Über Sinn und Zweck dieser Konfiguration brauchen wir nicht zu diskutieren – Fakt ist, dass sich das nicht auf die Schnelle ändern lässt.
Ich wollte nun dieses Projekt in TeamCity integrieren, so dass sowohl die Tests von NUnit als auch von xUnit ausgeführt werden. xUnit muss in TeamCity zwangsweise über ein MSBuild-Script integriert werden, was aber einfach zu bewerkstelligen ist. NUnit wird hingegen von TeamCity nativ unterstützt.
Mein erster Ansatz war nun, für den Buildprozess zwei Buildsteps anzulegen. Einmal für NUnit (nativ), einmal für xUnit (msbuild). Problem dabei: wenn einer der beiden Buildsteps fehl schlägt, wird der nächste Buildstep ebenfalls nicht ausgeführt. Das bedeutet also, dass man mit einem Buildlauf nicht die komplette Fehlerliste enthält, sondern immer nur die, des gerade laufenden Unittest-Frameworks (treten Fehler erst im zweiten Buildstep auf, ist dies natürlich egal).

“Schuld” an diesem Verhalten ist die Option Fail build if: at least one test failed. Ist diese nämlich aktiviert (Standardeinstellung), werden nachfolgende Buildsteps beim ersten auftretenden Fehler nicht mehr ausgeführt – was ja auch durchaus Sinn macht. Deaktiviert man die Option hingegen, werden alle Buildsteps ausgeführt, auch wenn in den Unittests Fehler aufgetreten sind. Problem dabei: Der Build wird auch bei aufgetretenen Fehlern in den Unittests grün mit “OK” markiert. Somit bekommt bei fehlgeschlagenen Unittests keiner eine Benachrichtigung über etwaige Fehler.

Ich entschloss mich zu folgendem Ansatz, der mit (N)Ant auf jeden Fall funktioniert hätte: Unser Test-Teilprojekt bekommt ein eigenes MSBuild-File, dass nur die Targets für das Ausführen der NUnit- bzw. xUnit-Tests enthält. Glücklicherweise gibt es von der .NET-Community bereits einige vorgefertigte Tasks, die das bewerkstelligen.

Zuerst müssen die MSBuild Community Tasks installiert werden (letztes Release). Dass die Dependencies zu den Unittest-Frameworks (xunit.runner.msbuild.dll) im Projektverzeichnis vorhanden sind, setze ich einfach mal voraus. Im Projekt mit den ganzen Unittests wird nun eine neue .msbuild-Datei mit folgendem Inhalt erstellt:

<Project DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<UsingTask AssemblyFile="$(MSBuildProjectDirectory)..DependenciesLibsxunit.runner.msbuild.dll" TaskName="Xunit.Runner.MSBuild.xunit" />
	<Import Project="$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets"/>
	<PropertyGroup>
		<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
	</PropertyGroup>
	<ItemGroup>
		<CompiledAssemblies Include="$(MSBuildProjectDirectory)bin$(Configuration)YourTestProject.dll" />
	</ItemGroup>

	<Target Name="Build">
		<MSBuild Projects="YourTestProject.csproj" Targets="Build" Properties="Configuration=$(Configuration)"></MSBuild>
	</Target>

	<Target Name="Test-xUnit">
 		<xunit assembly="@(CompiledAssemblies)"  ContinueOnError="true" />
	</Target>

	<Target Name="Test-NUnit">
		<NUnit Assemblies="@(CompiledAssemblies)" ContinueOnError="true" />
	</Target>

	<Target Name="TestOnly" DependsOnTargets="Test-xUnit;Test-NUnit">
	</Target>

	<Target Name="Test" DependsOnTargets="Build;Test-xUnit;Test-NUnit">
	</Target>
</Project>

Das gesamte Projekt ist so strukturiert, dass verschiedene Unterprojekte in die Solution integriert sind. Das Test-Projekt ist abhängig von den anderen Projekten. Diese Informationen liegen in YourTestProject.csproj und hängen natürlich von den Projektgegebenheiten ab.
Im UsingTask-Tag muss noch der korrekte Pfad zur xunit.runner.msbuild.dll hinterlegt werden.
Wenn nun das Target Test bzw. TestOnly aufgerufen wird, werden beide Unittesting-Frameworks aufgerufen. Wenn eines von beiden fehl schlägt, wird trotzdem weitergermacht (ContinueOnError=”true”).

Weiter geht es: Als erster bzw. zweiter Buildstep wird nun Test bzw. TestOnly als Target aufgerufen. Sieht dann so aus:

Sobald nun der Build ausgeführt wird, werden die Ergebnisse von xUnit und NUnit aggregiert und als ein Ergebnis dargestellt. Das bedeutet also, dass es keinen Unterschied mehr macht, aus welchem Framework die Tests stammen. Sollte nun ein Test fehlschlagen, werden alle anderen Tests trotzdem ausgeführt und es findet kein Abbruch statt. Mission accomplished!

.NET aus der Sicht eines Java-Entwicklers

Die letzten beiden Tage war ich damit beschäftigt, ein paar Evaluierungen für die .NET-Plattform zu machen. Eines unserer Projekte greift mit Hilfe eines (zugegebenermaßen ziemlich coolen) WPF-Frontends über WCF auf einen SOAP-Service zu, der die Verbindung zu einer MS SQL-Datenbank herstellt.

Logging auf die Konsole

Unter Java bzw. innerhalb eines Application-Servers ist es kein Problem, ein simples System.out.println() oder eben log.debug() zu benutzen. Die Ausgaben erscheinen dann jeweils in der Konsole der Entwicklungsumgebung. Wer jetzt denkt: so etwas geht doch sicherlich auch mit ASP.NET, der irrt gewaltig. Wenn innerhalb einer ASP.NET-Anwendung – hier zähle ich unseren WCF-Service einfach mal dazu – auf die Konsole geloggt wird (per log4nets Log.Debug oder aber Console.Write()), verschwinden die Ausgaben im Nirvana. Grund dafür ist, dass der IIS keine Konsole zur Verfügung stellt, in die überhaupt geloggt werden kann.

Entweder man benutzt nun die Trace-Methoden oder aber man schreibt die Logging-Ausgaben von log4net in eine Datei und setzt auf diese dann ein tail ab.

Lebenszyklus eines WCF-Service

Als JEE-Entwickler weiß ich, dass ein Servlet mit dem Start der Anwendung einmalig initalisiert wird und das Servlet dann so lange im Arbeitsspeicher gehalten wird, bis die Applikation heruntergefahren wird.

Bei WCF wird das Attribut InstanceContextMode benutzt, um die Gültigkeit eines Objekts zu beschreiben. Für einen WCF-Service gilt die Standardeinstellung, dass das Objekt (der Service) so lange vorgehalten wird, bis die gegenwärtige Session beendet wird (Einstellung PerSession).

Reihenfolge der Initalisierung von Servlets / Initialkonfiguration

In Anwendungen, die ich nicht mit Spring geschrieben habe, habe ich in meiner web.xml immer ein Servlet definiert, dass nichts weiter macht, als das die Standardeinstellungen gesetzt und z.B. der Logger initalisiert wird.

Bei .NET gibt es so etwas in der Art nicht (sollte ich falsch liegen, bitte ich um baldige Korrektur). Stattdessen muss man über eine selbst definierte ServiceHostFactory diese Konfiguration durchführen. Möglich wäre so etwas:

namespace My.Namespace.Service
{
    public class CustomServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
    {
        private Boolean isInitialized = false;

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(constructorString, baseAddresses);
        }

        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(serviceType, baseAddresses);
        }

        private void initalize()
        {
            if (isInitialized)
            {
                return;
            }

            log4net.Config.BasicConfigurator.Configure();
           // Weitere Initialisierungen
        }
    }
}

In der jeweiligen .svc-Datei muss dann die Instanzierung des Service über die CustomServiceHostFactory geschehen:

<%@ ServiceHost Language="C#" Debug="true" Factory="My.Namespace.Service.CustomServiceHostFactory" Service="My.Namespace.Service" CodeBehind="Service.svc.cs" %>

Service-Verbindungen im Client

Hinten über bin ich gefallen, als ich in der app.config des Clients probierte, das Suffix des Service-Endpoints einmalig über

<add key="httpBaseAddress" value="http://localhost" />

zu definieren. Das Attribut httpBaseAddress ist nur auf der Serverseite verfügbar und führt dazu, dass Redundanzen durch doppelte URIs entstehen.
Korrekt schaut das Beispiel nun so aus:

<configuration>
    <appSettings>
    </appSettings>
    <system.serviceModel>
      <!-- CusomtBinding benutzen, da mit wsHttpBinding NTLM nicht korrekt funktioniert -->
        <bindings>
          <customBinding>
            <binding name="Binding_Service">
              <mtomMessageEncoding />
              <security authenticationMode="SecureConversation">
                <secureConversationBootstrap authenticationMode="SspiNegotiated" />
              </security>
              <httpTransport authenticationScheme="Ntlm"/>
            </binding>
          </customBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:52264/Service.svc"
              binding="customBinding" bindingConfiguration="Binding_Service"
              contract="My.Namespace.Service.IService" name="Binding_IService">
            <identity>
              <dns value="localhost" />
            </identity>
          </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Im Client wird dann über

ChannelFactory ServiceFactory = new ChannelFactory("Binding_IService");

der Proxy des Services geladen.

Die obige Konfiguration (customBinding) zeigt übrigens den Zugriff auf den Web-Service via NTLM. Mit dem Binding wsHttpBinding funktioniert NTLM nämlich nicht.

ADUaCET released

A few minutes ago I released ADUaCET (Active Directory User and Computer Employee Tab) on sourceforge.net.
With ADUaCET the Microsoft Active Directory User and Computer MMC will be extended so that you are able store the image and employee number of users in Active Directory.

Storing user photos in Active Directory

One year ago I talked with the attorney of my confidence about a small tool which should extend the Active Directory Users and Computers MMC for storing and viewing images of users. This task passed out of my mind because of money-making work 😉

Today I extended my TwitterKicker JavaScript/jQuery application and considered about viewing the images of the players. As a pragmatic programmer I was not satisfied by the idea of storing the images manually and assign every image to a user by hand. It would be nice to ask a small webservice by the user image – the user is identified by his or her account name.

So i fired up my Visual Studio – It is a shame that no one ever developed a working image-to-Active-Directory-tool – and got the point: After some time I implemented a standalone proof-of-concept with C#. Tomorrow I will try to integrate the code into MMC snap-in extension. After that I will publish the source and binaries on sourceforge.net.

Got frickelfactor +1.

USB-Programmierung unter Windows

Ich benutze im ckl-net udev – für Windows habe ich ein ähnliches Framework dazu leider nicht gefunden.
Deshalb hier ein paar Infos, wie man denn auf Statuswechsel bei USB-Geräten reagiert: Der Schlüssel dafür ist WM_DEVICECHANGE – siehe dazu http://www.toolbox-mag.de/data/tx22005artikel1.pdf, Unter C++ ist dieser Artikel hilfreich (inkl. Source): http://www.codeproject.com/KB/system/HwDetect.aspx, Unter C# lohnt sich ein Blick auf http://www.mycsharp.de/wbb2/thread.php?threadid=29802, http://www.codeproject.com/KB/system/DriveDetector.aspx und besonders http://www.matze-friedrich.de/development/devnotify.aspx.

ServiceConsoleControl

Gestern habe ich ein alt-bekanntes Problem gelöst. Des öfteren muss ich über die Konsole Windows-Dienste neu starten. Da “net restart” nicht existiert, muss man auf

net stop $SERVICE && net start $SERVICE

zurückgreifen.
Außerdem haben wir in unseren Backup-Scripts einige Dienste, die Abhängigkeiten zu anderen Diensten haben (z.B. McAffe EPO, das von MSSQL abhängt). Hier wäre der pragmatische Lösungsansatz

net stop "McAfee Alert Manager" >> %log%
net stop "McAfee ePolicy Orchestrator 3.6.1 Application Server" >> %log%
net stop "McAfee ePolicy Orchestrator 3.6.1 Server" >> %log%
net stop "McAfee ePolicy Orchestrator 3.6.1 Event Parser" >> %log%
net stop "McAfee Outbreak Manager" >> %log%
net stop "McAfee Log Service" >> %log%
net stop "McAfee SpamKiller" >> %log%
net stop "SQLAgent$EPOSERVER" >> %log%
net stop "MSSQL$EPOSERVER" >> %log%

REM Am Ende alles wieder starten

Viel einfacher wäre ein einfaches

net stop MSSQL$EPOSERVER

wodurch alle von diesem Dienst abhängigen Dienste ebenfalls beendet werden.

Soweit so gut, ich habe mich gestern also hingesetzt und das ganze in C# heruntergetippert. scc (angelehnt an sc) unterstützt die Befehle “start, stop und restart”. Der Code dafür lautet:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceProcess;
using System.Threading;

namespace ServiceConsoleControl
{
    /// <summary>
    /// Main entry class
    /// </summary>
    class Program
    {
        private static String NAME = "scc";

        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                ShowUsageAndExit(null);
            }

            try
            {
                Command cmd = Command.Factory(args[0]);
                cmd.SetServiceName(args[1]);
                cmd.Execute();
            }
            catch (Exception e)
            {
                ShowUsageAndExit("Error: " + e.Message);
            }
        }
        /// <summary>
        /// Shows the usage of this program and exits with code 0
        /// </summary>
        /// <param name="_line">additional line to print before usage is shown</param>
        public static void ShowUsageAndExit(String _line)
        {
            if (_line != null)
            {
                Program.LogLine(_line);
            }

            Program.LogLine(Program.NAME + " starts, stops and restarts services with all dependencies to other services");
            Program.LogLine("Usage: " + Program.NAME + " [" + Command.CMD_START + ", " + Command.CMD_STOP + ", " + Command.CMD_RESTART + "] [Service]");
            Environment.Exit(0);
        }

        /// <summary>
        /// Write string to console
        /// </summary>
        /// <param name="_msg">string to write</param>
        public static void LogLine(String _msg)
        {
            Console.WriteLine(_msg);
        }
    }

    /// <summary>
    /// Interface for executable service commands
    /// </summary>
    interface IServiceCommand
    {
        /// <summary>
        /// Execute the command
        /// </summary>
        void Execute();

        /// <summary>
        /// Sets name of service
        /// </summary>
        /// <param name="_name">name of service</param>
        void SetServiceName(String _name);

        /// <summary>
        /// Retrieves name of service
        /// </summary>
        /// <returns>name of service</returns>
        string GetServiceName();
    }

    /// <summary>
    /// Abstract class for commands, implements IServiceCommand
    /// </summary>
    abstract class Command : IServiceCommand
    {
        private String _name;

        protected ServiceController sc = new ServiceController();

        public static String CMD_STOP = "stop";

        public static String CMD_START = "start";

        public static String CMD_RESTART = "restart";

        /// <summary>
        /// Factory method for command.
        /// Given command will be lowered.
        /// If the command is not supported, an exception will be thrown
        /// </summary>
        /// <param name="_cmd">Command to execute - this can be one of this.CMD_*</param>
        /// <returns>Instance of Command</returns>
        public static Command Factory(String _cmd)
        {
            _cmd = _cmd.ToLower();

            if (_cmd.Equals(CMD_RESTART))
            {
                return new RestartCommand();
            }
            else if (_cmd.Equals(CMD_START))
            {
                return new StartCommand();
            }
            else if (_cmd.Equals(CMD_STOP))
            {
                return new StopCommand();
            }

            throw new Exception("Unsupported command: " + _cmd);
        }

        /// <summary>
        /// Sets service name, required by IServiceCommand
        /// </summary>
        /// <param name="_serviceName">Service name to set</param>
        public void SetServiceName(String _serviceName)
        {
            _name = _serviceName;
            sc.ServiceName = _serviceName;
        }

        /// <summary>
        /// Gets service name, required by IServiceCommand
        /// </summary>
        /// <returns>Service name</returns>
        public String GetServiceName()
        {
            return _name;
        }

        /// <summary>
        /// Abstract execute command which is required by IServiceCommand
        /// </summary>
        abstract public void Execute();
    }

    /// <summary>
    /// Abstract class for recursive commands, can be used for solving dependencies between services
    /// </summary>
    abstract class RecursiveCommand : Command
    {
        /// <summary>
        /// Depth of dependency
        /// </summary>
        private int _depth = 0;

        /// <summary>
        /// Stringbuilder with prefixing spaces
        /// </summary>
        private StringBuilder sb;

        /// <summary>
        /// Timespan to wait for service
        /// </summary>
        protected TimeSpan tsWait = new System.TimeSpan(10000000000000);

        /// <summary>
        /// Abstract method which retrieves the recursive command
        /// </summary>
        /// <returns>RecursiveCommand</returns>
        abstract protected RecursiveCommand GetCommand();

        /// <summary>
        /// Depth value
        /// </summary>
        public int Depth
        {
            set { _depth = value; }
            get { return _depth; }
        }

        /// <summary>
        /// Return depth as spaces
        /// </summary>
        /// <returns>String with depth as spaces</returns>
        protected String GetSpaces()
        {
            if (sb == null)
            {
                sb = new StringBuilder();

                for (int i = 0; i < _depth; i++)
                {
                    sb.Append("  ");
                }
            }

            return sb.ToString();
        }

        /// <summary>
        /// Executes GetCommand() on all given ServiceController[]
        /// </summary>
        /// <param name="scs">Array of ServiceController on which the GetCommand() will be executed</param>
        protected void ExecuteCommandOnServiceControllers(ServiceController[] _scs)
        {
            Program.LogLine(GetSpaces() + "Service dependencies: " + _scs.Length);

            for (int i = 0, m = _scs.Length; i < m; i++)
            {
                RecursiveCommand rc = this.GetCommand();
                rc.Depth = this.Depth + 1;
                rc.SetServiceName(_scs[i].ServiceName);
                rc.Execute();
            }
        }

        /// <summary>
        /// Returns true if service is of ServiceType Win32ShareProcess or Win32OwnProcess. Other types cannot be killed
        /// </summary>
        protected bool IsKillable
        {
            get
            {
                if (sc.ServiceType.Equals(ServiceType.Win32ShareProcess) || sc.ServiceType.Equals(ServiceType.Win32OwnProcess))
                {
                    return true;
                }

                Program.LogLine(GetSpaces() + this.DisplayName + " is of type " + sc.ServiceType.ToString() + ". These services can not be processed.");
                return false;
            }
        }

        /// <summary>
        /// Returns display name
        /// </summary>
        public String DisplayName
        {
            get
            {
                return "Service " + sc.DisplayName + " (" + sc.ServiceName + ")";
            }

        }
    }

    /// <summary>
    /// Command for restarting a service
    /// </summary>
    class RestartCommand : Command
    {
        /// <summary>
        /// Stops the service and then restarts
        /// </summary>
        public override void Execute()
        {
            IServiceCommand stopCmd = new StopCommand();
            IServiceCommand startCmd = new StartCommand();
            stopCmd.SetServiceName(this.GetServiceName());
            startCmd.SetServiceName(this.GetServiceName());

            stopCmd.Execute();
            startCmd.Execute();
        }

        public override String ToString()
        {
            return CMD_RESTART;
        }
    }

    /// <summary>
    /// Command for starting a service with all its dependencies
    /// </summary>
    class StartCommand : RecursiveCommand
    {
        public override String ToString()
        {
            return CMD_START;
        }

        /// <summary>
        /// Retrieve a new instance of myself
        /// </summary>
        /// <returns></returns>
        protected override RecursiveCommand GetCommand()
        {
            return new StartCommand();
        }

        /// <summary>
        /// Starts a service. The service could only be started if it is Win32[Own|Share]Process. KernelDriver or other types of services are not allowed.
        /// At first all services will be started which this service depends on. At second the service itself will be started.
        /// After that all services are started which depends on this service.
        /// </summary>
        public override void Execute()
        {
            if (this.IsKillable)
            {
                if (sc.Status.Equals(ServiceControllerStatus.Stopped))
                {
                    Program.LogLine(GetSpaces() + "Starting " + this.DisplayName);
                    Program.LogLine(GetSpaces() + "Starting all services that " + this.DisplayName + " depends on ...");
                    ExecuteCommandOnServiceControllers(sc.ServicesDependedOn);
                    sc.Start();
                    sc.WaitForStatus(ServiceControllerStatus.Running, tsWait);
                    Program.LogLine(GetSpaces() + "Service is now in status " + sc.Status.ToString());
                    Program.LogLine(GetSpaces() + "Starting all services that depends on " + this.DisplayName + " ... ");
                    ExecuteCommandOnServiceControllers(sc.DependentServices);
                }
                else
                {
                    Program.LogLine(GetSpaces() + this.DisplayName + " has status " + sc.Status.ToString() + " - nothing to do");
                }
            }
        }
    }

    /// <summary>
    /// Command for stopping a service with all its dependencies
    /// </summary>
    class StopCommand : RecursiveCommand
    {
        public override String ToString()
        {
            return CMD_STOP;
        }

        /// <summary>
        /// Returns an instance of myself
        /// </summary>
        /// <returns>New instance of StopCommand</returns>
        protected override RecursiveCommand GetCommand()
        {
            return new StopCommand();
        }

        /// <summary>
        /// Executes the command. The service could only be stopped if it is Win32[Own|Share]Process. KernelDriver or other types of services are not allowed.
        /// At first all services will be stopped that depends on this service. Then this service is stopped.
        /// </summary>
        public override void Execute()
        {
            if (this.IsKillable)
            {
                if (sc.Status.Equals(ServiceControllerStatus.Running))
                {
                    Program.LogLine(GetSpaces() + "Stopping " + this.DisplayName);
                    Program.LogLine(GetSpaces() + "Stopping all services that depends on " + this.DisplayName + " ...");
                    ExecuteCommandOnServiceControllers(sc.DependentServices);
                    sc.Stop();
                    sc.WaitForStatus(ServiceControllerStatus.Stopped, tsWait);
                    Program.LogLine(GetSpaces() + "Service is now in status " + sc.Status.ToString());
                }
                else
                {
                    Program.LogLine(GetSpaces() + this.DisplayName + " has status " + sc.Status.ToString() + " - nothing to do");
                }
            }
        }
    }
}