Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# How to properly unit test a class that follows a decorator pattern?

I'm fairly new to unit testing(I'm actually studying it as we speak)

My goal is of course to be able to test the method inside the class below.

The class simply checks if the input is already in the cache, if the input is not in the cache, it will return the reversed form of the input(though the implementation is not here, but assuming it does, since the purpose is just to test).

Basically the goal is to make sure the if-else is tested.

Here is my class:

namespace YouSource.Decorator
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    /// <summary>
    /// Caching Decorator
    /// </summary>
    public class CachingDecorator : IModifyBehavior
    {
       private IModifyBehavior behavior;

       private static Dictionary<string, string> cache = 
           new Dictionary<string, string>();

        public string Apply(string value)
        {
            ////Key = original value, Value = Reversed
            var result = string.Empty;

            //cache.Add("randel", "lednar");
            if(cache.ContainsKey(value))
            {

                result = cache[value];

            }
            else
            {

                result = this.behavior.Apply(value);// = "reversed";
                cache.Add(value, result); 
            }
            return result;
        }
    }
}

Here is the current code of my test:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YouSource.Decorator.Tests
{
    [TestClass]
    public class CachingDecoratorTest
    {
        private IModifyBehavior behavior;

        [TestInitialize]
        public void Setup()
        {
            this.behavior = new StubModifyBehavior(new CachingDecorator());
        }

        [TestCleanup]
        public void Teardown()
        {
            this.behavior = null;
        }

        [TestMethod]
        public void Apply_Cached_ReturnsReversedCachedValue()
        {
            string input = "randel";
            string reversed = "lednar";
            Assert.AreEqual(reversed, this.behavior.Apply(input));
        }

        [TestMethod]
        public void Apply_NotCached_ReturnsReversed()
        {
            string input = "not cached";
            string reversed = "reversed";
            Assert.AreEqual(reversed, this.behavior.Apply(input));
        }

        public class StubModifyBehavior : IModifyBehavior
        {
            private IModifyBehavior behavior;

            public StubModifyBehavior(IModifyBehavior behavior)
            {
                this.behavior = behavior;
            }

            public string Apply(string value)
            {

                //return this.behavior.Apply(value);
            }
        }
    }
}
like image 375
Randel Ramirez Avatar asked Mar 12 '13 17:03

Randel Ramirez


1 Answers

Decorator dynamically attaches new behavior to object which is decorated. That is responsibility of decorator. That is what you should test.

So, lets write caching decorator for following component:

public interface IComponent
{
    string DoSomething(string value);
}

Creating test fixture (or TestClass, if you are using MSTest)

[TestFixture]
public class CachingComponentTests
{
    private CachingComponent _cachingComponent;
    private Mock<IComponent> _componentMock;

    [SetUp]
    public void Setup()
    {
        _componentMock = new Mock<IComponent>(); // using Moq in this sample
        _cachingComponent = new CachingComponent(_componentMock.Object);
    }
}

For this code to compile you need to create CachingComponent class, which accepts decorated component. Something like that (little speed up here):

public class CachingComponent : IComponent
{
    private IComponent _component;

    public CachingComponent(IComponent component)
    {            
        _component = component;
    }

    public string DoSomething(string value)
    {
        throw new NotImplementedException();
    }
}

Now let's define expected behavior of decorator. It should pass call to component, if that is first call with some parameter:

[Test]
public void ShouldCallComponentWhenCalledFirstTime()
{
    _componentMock.Setup(c => c.DoSomething("foo")).Returns("bar");

    Assert.That(_cachingComponent.DoSomething("foo"), Is.EqualTo("bar"));
    _componentMock.Verify();
}

And it fails thus method is not implemented yet. First implementation (well, simplest implementation will be returning null, but we are moving little faster here):

    public string DoSomething(string value)
    {
        return _component.DoSomething(value);
    }

Nice. Our caching component works as expected. But it is not caching anything. Writing test for that. It should call component only once. All further calls should return cached value:

[Test]
public void ShouldReturnCachedValueWhenCalledMoreThanOnce()
{
    _componentMock.Setup(c => c.DoSomething("foo")).Returns("bar");

    Assert.That(_cachingComponent.DoSomething("foo"), Is.EqualTo("bar"));
    Assert.That(_cachingComponent.DoSomething("foo"), Is.EqualTo("bar"));
    _componentMock.Verify(c => c.DoSomething("foo"), Times.Once());
}

And implementation:

public class CachingComponent : IComponent
{
    private Dictionary<string, string> _cache = new Dictionary<string, string>();
    private IComponent _component;

    public CachingComponent(IComponent component)
    {            
        _component = component;
    }

    public string DoSomething(string value)
    {
        if (!_cache.ContainsKey(value))            
            _cache.Add(value, _component.DoSomething(value));            

        return _cache[value];
    }
}

Now you have caching decorator with verified behavior.

like image 102
Sergey Berezovskiy Avatar answered Sep 22 '22 04:09

Sergey Berezovskiy