Posts Tagged ‘Unit tester’

Lite stulna punkter att gå efter!

2013-03-31

Jag rekomenderar att läsa hela artiklen här: https://github.com/thomasdavis/best-practices

För eget intresse tog jag ut några punkter jag fann intressanta!

 

Don’t do hard things, do easy things.

  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Readability counts.
  • If the implementation is hard to explain, it’s a bad idea.
  • If the implementation is easy to explain, it may be a good idea.

Refactoring > Rewriting

Common Excuses For A Software Rewrite

  1. The Code Sucks
  2. ”We’re So Much Smarter Now”
  3. We Picked The Wrong Platform/Language

Why Rewriting Is (Almost) Never A Good Idea

  1. It Will Take Longer Than You Think
  2. Markets Change
  3. Existing Customers Become Frustrated
  4. Refactoring Can Cleanup The Code
  5. You Don’t Control The Rewrite, It Controls You

To write effective unit tests, you need to write testable code

Flaw #1: Constructor does Real Work

Warning Signs

  • new keyword in a constructor or at field declaration
  • Static method calls in a constructor or at field declaration
  • Anything more than field assignment in constructors
  • Object not fully initialized after the constructor finishes (watch out forinitialize methods)
  • Control flow (conditional or looping logic) in a constructor
  • Code does complex object graph construction inside a constructor rather than using a factory or builder
  • Adding or using an initialization block

Flaw #2: Digging into Collaborators

  • Objects are passed in but never used directly (only used to get access to other objects)
  • Law of Demeter violation: method call chain walks an object graph with more than one dot (.)
  • Suspicious names: context, environment, principal, container, or manager

Flaw #3: Brittle Global State & Singletons

Warning Signs

  • Adding or using singletons
  • Adding or using static fields or static methods Adding or using static initialization blocks Adding or using registries
  • Adding or using service locators

Flaw #4: Class Does Too Much

Warning Signs

  • Summing up what the class does includes the word “and”
  • Class would be challenging for new team members to read and quickly “get it” Class has fields that are only used in some methods
  • Class has static methods that only operate on parameters

Lättlästa unit-tester med ”extension methods”

2013-03-20

Detta inlägg kommer ge er en beskrivning på hur jag kontrollerar utfallet (assert) av mina tester så de är lättlästa och lätta att underhålla. NUnit är det ramverk jag använder mig av och ni kan hitta det på Nuget.

Standard

Om ni har använt er av NUnit tidigare så vet ni hur det fungerar när man skall kontrollera utfallet. Man måste hela tiden anropa den statiska klassen ”Assert” när man vill testa något. Så som exemplet nedan:

NUnit   
  1. [Test]
  2. public void Test_Int()
  3. {
  4. var toTest = 4;
  5. Assert.AreEqual(4, toTest);
  6. }

Yoda_1

När man läser detta från väster till höger, som man är van vid att göra i vanliga texter, så påminner det mig om Yoda från StarWars. Han är inte direkt känd för att ha den bästa meningsbyggnaden precis. Dessutom har det hänt att man placerar parametrarna fel i anropet, vilket kan leda till lite frågetecken om testet inte skulle gå igenom och man läser felmeddelandet.

Extension

Min lösning på problemet är att använda ”Extensionmethods” som introducerades till C# i version 3.0. Om vi tar samma exempel så ser det ut såhär istället:

Mitt test   
  1. [Test]
  2. public void Test_Int()
  3. {
  4. var toTest = 4;
  5. toTest.ShouldEqual(4);
  6. }

Själva ”extension”-klassen ser ut på följande sätt:

Extension   
  1. public static class NUnitExtensions
  2. {
  3. public static void ShouldEqual(this object a, object b, string message = "")
  4. {
  5. Assert.AreEqual(b, a, message);
  6. }
  7. }

Personligen tycker jag att det är viktigt att skriva lättläst kod även om det betyder en extra klass som tar hand om alla ”extension”-metoder. En annan fördel med detta är att man inte bara behöver testa värdet som skickas in, utan man kan också förändra det. Exempelvis dess typ.

Blanda in ”Generics”

Om vi sedan blandar in ”Generics” i bilden så kan man enkelt få ”extension”-metoderna att retunera värdet och på så sätt fortsätta testa det. Se exemplet nedan:

Testet   
  1. [Test]
  2. public void Test_Int()
  3. {
  4. var toTest = new List<User>
  5. {
  6. new User { Name = "Dennis" }
  7. };
  8. toTest.ShouldBeCountedTo(1)
  9. .And()
  10. .First().Name.ShouldEqual("Dennis");
  11. }

Observera metoden ”And()”. Dess syfte är inte att testa något utan endast att göra ”Assert”:en ännu mer lättläst.  Det är dock en smaksak om man ska använda eller hur ofta man använder den metoden. Kollat vi på ”extension”-metoderna som används i detta exemplet så ser de ut såhär:

Testare   
  1. public static T ShouldNotBeNull<T>(this T a)
  2. {
  3. Assert.IsNotNull(a);
  4. return a;
  5. }
  6. public static IEnumerable<T> ShouldBeCountedTo<T>(this IEnumerable<T> a, int count)
  7. {
  8. a.ShouldNotBeNull();
  9. if(a.Count() != count)
  10. {
  11. throw new Exception(string.Format("Count failed! Expected: {0} but was: {1}", count, a.Count()));
  12. }
  13. }
  14.  
  15. public static T And<T>(this T a)
  16. {
  17. return a;
  18. }

Typa om

En annan sak man kan göra med denna typ av testning är att typa om ett värde samtidigt som man testar det, för att därefter retunera värdet av den rätta typen. Det kan exempelvis se ut såhär:

Testet   
  1. [Test]
  2. public void Test_Int()
  3. {
  4. object toTest = User { Name = "Dennis" };
  5. toTest.ShouldBeOfType<User>()
  6. .And()
  7. .Name.ShouldEqual("Dennis");
  8. }

Som ni kan se så sparar jag ”User” objectet i en ”object”-typad variabel, vilket betyder att man inte når ”Name” egenskapen innan man typat om den.

Testare   
  1. public static T ShouldBeType(this object a)
  2. {
  3. try
  4. {
  5. var tmp = (T)a;
  6. return tmp;
  7. }
  8. catch (Exception)
  9. {
  10. throw new Exception(string.Format("Expexted type: {0}. But was: {1}", typeof(T).Name, typeof(a).Name));
  11. }
  12. }

Smidigt! Eller var tycker du?

Slutligen

Denna metoden gör inte bara att era tester blir lättare att läsa. Utan den gör också att det blir roligare att skriva testerna. Gör mig en tjänst och testa detta i ert nästa projekt!