lazy dev do not write mocks

Dlaczego dobry developer nie pisze mocków.

Mocki to zmora wielu osób bo często trzeba napisać sporo „niepotrzebnego” kodu, który czasem (jeśli dobierzemy zły framework) jest zupełnie nieczytelny. Na domiar tego niektórzy używają pojęć mock, stub, fake, dummy. Jak się połapać w tym wszystkim? Co do czego użyć? Jak to wszystko pokonfigurować? I wszystko po to aby przetestować jakąś jedną funkcję…

… no właśnie, dlatego nie piszę mocków. Unikam ich jak ognia.

No ale jak to nie piszę? Bo jestem leniwy i uważam, że dobry dev powinien być leniwy. Zamiast pisać kolejny raz to samo warto zastanowić się czy nie istnieje lepszy sposób. Tym właśnie sposobem trafiłem na genialny wynalazek po tytułem AutoData. Zresztą nie tylko ja. Do napisania tego wpisu zainspirował mnie Jarek Stadnicki tym oto wpisem. Sam od dawna używam tego genialnego wynalazku i używam go do… no właśnie do niepisania mocków. AutoData potrafi sam generować to co potrzebujemy. Ponieważ moje ulubione narzędzia to C# i NSubstitute to poniższe przykłady będą po C#powemu w narzeczu NSubstitutowym. Zatem do rzeczy:

Instalujemy wszystko co potrzebne (w projekcie testowym, więc jeśli zaczynasz od zera to uruchamiasz dotnet new xunit):

dotnet add package nsubstitute
dotnet add package autofixture.autonsubstitute
dotnet add package autofixture.xunit2

A mając to napiszmy sobie test:

        [Theory, AutoNSubstitute]
        public void AutoData_create_mocks_for_me(ISomeDependency dependency, ISomeOtherDependency otherDependency, ISomeArg exampleArgs, ILog log)
        {
			var sut = new SystemUnderTest(dependency, otherDependency, log);
			
			sut.MethodToTest(exampleArgs);

			dependency.Received(1).Hello(exampleArgs);
			otherDependency.Received(1).Hello(exampleArgs);
        }

Co tutaj się dzieje? Atrybut AutoNSubstitue mówi, że mają się wygenerować mocki dla argumentów. Uruchomienie tego testu spowoduje, że zostaną stworzone mocku (za pomocą NSubstitute) i wrzucone jako argumenty wywołania. I możemy zacząć ich używać bez zbędnego pisania. Powyższy przykładowy test sprawdza czy zależności w testowanym komponencie zostały wywołane z odpowiednim argumentem. Powiesz, że to jest naiwny test? No pewnie, że jest naiwny jednak jest wiele systemów, które niestety tak są pisane. Jeśli sami będziemy konstruować mocki to możemy niezauważyć, że SystemUnderTest to tak naprawdę jakiś wraper, który przekazuje dane dalej. Mając kod „odmockowany” widać to jak na dłoni…. WIN WIN 🙂 Czystszy i czytelniejszy kod powoduje, że łatwiej widać absurdy architektury – ale to już inna historia.

Wracając do tematu. Cała magia tutaj dzieje się w atrybucie AutoNSubstitute. Ten zaś nie należy do standardowych klocków NSubstituta. Sam go wydziergałem (na podstawie wpisów autora biblioteki AutoData Marka Seemanna). A sam kod tego cuda tutaj:

public class AutoNSubstituteAttribute: AutoDataAttribute
	{
		public AutoNSubstituteAttribute()
			: base(()=>new Fixture()
				.Customize(new AutoPopulatedNSubstitutePropertiesCustomization())
				)
		{
		}
	}

    internal class AutoPopulatedNSubstitutePropertiesCustomization
	: ICustomization
	{
		public void Customize(IFixture fixture)
		{
			fixture.ResidueCollectors.Add(
				new Postprocessor(
					new NSubstituteBuilder(
						new MethodInvoker(
							new NSubstituteMethodQuery())),
					new AutoPropertiesCommand(
						new PropertiesOnlySpecification())));
		}

		private class PropertiesOnlySpecification : IRequestSpecification
		{
			public bool IsSatisfiedBy(object request)
			{
				return request is PropertyInfo;
			}
		}
	}

Cały kod do tego wpisu znajdziesz tutaj.