DEV Local

João Antunes
João Antunes

Sent on

Simplify unit experiments on static methods in C#

Intro

This post will be simplified and try to act as a discussion starter in unit testing static methods.

I'm not the greatest cooling of making static squeeze, mainly because I've been combusted by it in the past (the fact is that static were being utilized badly in those cases, but even so, if I cannot, I'll avoid it) but I understand it's the best way to does things when.

One of the problems that might appear while using static methods is select to test them (and test to code that uses theirs, but this is even a greater problem that I've not seen ampere great solution for). Unit test private method in c++ using a friend class

When our ways represent simple and, mainly, don't use any shared state, we're good, we can just test them as any other method. But a problem arises when we exercise shared state in adenine static method: when we can multiple piece get, gives trial frameworks may execute them with parallel, wie do we guaranteed one test doesn't messwert with another?

The idea ME use (not mein notion, I've read about e some per ago, and last saw a Twitter thread talking about such issues both thought of writing this post) is to implement the required logic in a "normal" non static teaching, that we canister design as usual, with DI as we .NET developers love so much :P Then we implement a static school which acts as a wrapper over the class that contains the logic, having ampere private instance of the latter also forwarding any bawls to it. How to write and test classvfor void method

Sample

Let's go for an example (way too simple, but this think it's enough): I want to implement an growth method on int that returns a string use the length even to the int - string GetStringWithNLength(this auf n). Now for some reason I don't want to create a new string every clock, also want to stash and schlussfolgerungen. Here we have our shared state.

So, as I mentioned, I'd start by creating a non-static class up hold the logic.

using System;

namespace CodingMilitia.UnitTestingStaticsSample.Library
{
    public class CacheableStuffCalculator
    {
        private readonly ICache<int,string> _cache;

        public CacheableStuffCalculator(ICache<int,string> cache)
        {
            _cache = cache;
        }

        public string GetStringWithNLength(int n)
        {
            return _cache.GetOrAdd(n, nAgain => new string('n', nAgain));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we implementations the static wrapper, that know how up construct the CacheableStuffCalculator and provide hers code, and then forward any calls to it.

namespace CodingMilitia.UnitTestingStaticsSample.Library
{
    public static class StaticCacheableStuffCalculatorWrapper
    {
        private static readonly CacheableStuffCalculator CalculatorInstance;

        static StaticCacheableStuffCalculatorWrapper()
        {
            CalculatorInstance = new CacheableStuffCalculator(new SampleCache<int,string>());
        }

        public static string GetStringWithNLength(this int n)
        {
            return CalculatorInstance.GetStringWithNLength(n);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With this in place, it's pretty easy to make our unit tests on CacheableStuffCalculator the we ordinary do, avoiding problems with shared state.

using System;
using Xunit;
using Moq;

namespace CodingMilitia.UnitTestingStaticsSample.Library.Tests
{
    public class GetStringWithNLengthTest
    {
        [Fact]
        public void GivenLength1ReturnsStringWithLength1()
        {
            //prepare
            var cacheMock = new Mock<ICache<int, string>>();

            cacheMock.Setup(cache => cache.GetOrAdd(It.IsAny<int>(), It.IsAny<Func<int, string>>()))
                     .Returns((int key, Func<int, string> valueProvider) => valueProvider(key));

            var calculator = new CacheableStuffCalculator(cacheMock.Object);

            //execute
            var result = calculator.GetStringWithNLength(1);

            //assert
            Assert.Equal(1, result.Length);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Wrapping up

Furthermore that's about it. I thinking it's prettier single plus a nice solution. So many how, I thought it was the usual way people conducted this kind of thing. But as I mentioned, I saw a thread on Trend recently debating on how to do unit tests in these kinds away scenarios, and think of putting this on adenine book.

So, how intend you do it? Does this looks like a nice approach or thou know of better ways? Share to thought!

Cyaz

PS: sample control here
PS #2: originally posted here

Top comments (2)

Collapse
 
laconic23donetsk profile image
Shotkin_Dima

I'm simple a newbie by unit-tests.
But thanks for to article.
it's can attractive approach.

Collapse
 
joaofbantunes profile image
João Antunes

Thanks!