ArchUnit Tests - Aufruf einer Methode prüfen



  • Hallo,

    ich versuche gerade durch ArchUnit durchzusteigen. Ich möchte gerne verhindern, dass die statische Methode string.SubString() in meiner Assembly aufgerufen werden kann. Die Beispiele für ArchUnit in Verbindung mit C# und MSTest sind relativ rar im Netz.

    Daher die Frage, geht das technisch und wenn ja wie? Ich habe folgende Klasse in der Assembly "Lib", die ich testen möchte (die Methode soll nur als Beispiel dienen).

    namespace Lib
    {
    	public class FirstClass
    	{
    		public static string MySubString(string input, int position)
    		{
    			return input.Substring(position);
    		}
    	}
    }
    

    Die Klasse zum Testen (Test-Assembly) sieht im Moment wie folgt aus:

    using ArchUnitNET.Domain;
    using ArchUnitNET.Fluent;
    using ArchUnitNET.Loader;
    using static ArchUnitNET.Fluent.ArchRuleDefinition;
    
    namespace LibTest
    {
    	[TestClass]
    	public class FirstClassTests
    	{
    		private static readonly Architecture Architecture = new ArchLoader().LoadAssemblies(System.Reflection.Assembly.Load("Lib")).Build();
    
    		// alle Klassen einer Assembly???
    		private readonly IObjectProvider<Class> AllLibClasses = Classes().That().ResideInAssembly(System.Reflection.Assembly.Load("Lib"));
    
    		[TestMethod]
    		public void TestIndexOf()
    		{
    			// kein IArchRuleInterface
    			IArchRule classesThatCallStringDotSubString = Classes().That().CallAny("SubString");
    
    			// Folgefehler
    			classesThatCallStringDotSubString.Check(Architecture);
    		}
    	}
    }
    

    Ich scheitere schon in Zeile 20, denn der Aufruf liefert keine Schnittstelle vom Typ <IArchRule> zurück. Die Beispiele, die ich im Netz finden konnte, helfen mir leider nicht wirklich weiter 😞

    Kann jemand helfen? Vielen Dank im Voraus.

    VG Torsten



  • Du meinst ArchUnitNET bzw. der UserGuide dazu?
    Deren API ist recht komplex, da sie aus sehr vielen (zumeist auch noch generischen) Klassen und Interfaces besteht.
    Nimm doch einfach mal var und schau dir im Debugger (bzw. Intellisense) dann den Typen an.



  • Hallo,

    wie du schon schreibst, es ist sehr komplex und schwer zu durchdringen. Der gelieferte Rückgabetyp ist

    TGivenRuleTypeConjunction
    

    Ich habe die Testmethode wie folgt erweitert:

    [TestMethod]
    public void TestIndexOf()
    {
    	// kein IArchRuleInterface
    	var classesThatCallStringDotSubString = Classes().That().CallAny("SubString");
    
    	// Folgefehler
    	//classesThatCallStringDotSubString.Check(Architecture);
    
    	var xx = classesThatCallStringDotSubString.Should().NotExist();
    	var yy = xx.Evaluate(Architecture).ToList();
    
    	Assert.IsTrue(yy[0].Passed);
    }
    

    Der Test ist lauffähig, was ist gut. Schlecht ist, dass er Pass liefert 😞

    Mir ist noch eine Sache aufgefallen:

    return input.Substring(position);
    

    ist äquivalent mit

    return input[position..];
    

    Frage wäre hier. Kann das ArchUnit-Framework so etwas abdecken?

    VG Torsten



  • Meinst du den Aufruf deiner eigenen Methode MySubString oder die String-Methode Substring (nicht "SubString")?
    Du kannst bei letzterem auch nameof(String.Substring) verwenden.

    Und input[x..y] sollte äquivalent zu `Substring(...) sein, s. Ranges: Adding Index and Range support to existing library types (letzter Absatz):

    • string: the method Substring will be used instead of Slice.


  • Hallo,

    ja, mein Fehler 😞

    Ich habe deinen Rat mit <nameof(String.Substring)> befolgt - keine "Magic Numbers" 😉

    Interessant ist die Beschreibung der Regel zur Laufzeit, die lautet nämlich "ArchRule = {Classes that calls any method with full name "Substring" should not exist}". Das klingt für mich absolut richtig - abgesehen vom "full name".

    Allerdings ist eine weitere Beschreibung "[0] = {There are no objects matching the criteria}". Und das finde ich merkwürdig, da ich ja genau den Aufruf mache, siehe:

    namespace Lib
    {
    	public class FirstClass
    	{
    		public static string MySubString(string input, int position)
    		{
    			//return string.Empty;
    			return input.Substring(position);
    		}
    	}
    }
    

    Es scheint also, als würde ich noch irgendetwas falsch machen.

    Mein Ziel ist es, dass die "Original String.Substring()" Methode nicht aufgerufen werden soll. Sondern nur meine eigene, da ich in meiner eigenden Funktion mehr Fehlerinformationen habe. Das ist mir in einem Fehlerfall sehr wichtig.

    VG Torsten



  • Dann verwende mal CallAny("System.String.Substring") (hier funktioniert dann nameof(...) leider nicht - C# bräuchte noch ein fullnameof(...) :-).

    Entsprechend ArchUnitNET.Fluent.Syntax.Elements.ObjectsShould< TRuleTypeShouldConjunction, TRuleType > Class Template Reference kannst du bei

    TRuleTypeShouldConjunction CallAny (string pattern, bool useRegularExpressions=false)
    

    auch alternativ ein Regex-Pattern verwenden.
    Oder noch spezieller mittels

    TRuleTypeShouldConjunction CallAny (MethodMember method, params MethodMember[] moreMethods)
    


  • G' Morgen,

    scheint so, als gäbe es noch einen anderen Weg - funktioniert aber auch irgendwie nicht:

    [TestMethod]
    public void TestIndexOf()
    {
    	// kein IArchRuleInterface
    	var classesThatCallStringDotSubString = Classes().That().CallAny("System.String.Substring");
    
    	var xxxxx = Classes().GetObjects(Architecture).ToList();
    	var yyyyy = xxxxx[0].Members.ToList();
    
    	var zzzzz0 = yyyyy[0].CallsMethod(".Substring");
    	var zzzzz1 = yyyyy[1].CallsMethod(".Substring");
    
    	// Folgefehler
    	//classesThatCallStringDotSubString.Check(Architecture);
    
    	var xx = classesThatCallStringDotSubString.Should().NotExist();
    	var yy = xx.Evaluate(Architecture).ToList();
    
    	Assert.IsTrue(yy[0].Passed);
    }
    

    Ich werde mal bei StackOverflow noch fragen. Danke für deine Hilfe 😉

    VG Torsten



  • Ich denke, daß dort der Punkt zuviel ist: CallsMethod("Substring")


Anmelden zum Antworten