简体   繁体   English

C#如何正确单元测试一个遵循装饰器模式的类?

[英]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. 基本上,目标是确保测试if-else。

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);
            }
        }
    }
}

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) 创建测试夹具(或TestClass,如果您使用的是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. 要编译此代码,您需要创建CachingComponent类,该类接受修饰组件。 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): 第一个实现(好吧,最简单的实现将返回null,但我们在这里移动得更快):

    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. 现在,您已经使用经过验证的行为缓存装饰器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM