C# 如何正确地对遵循装饰器模式的类进行单元测试?

标签 c# .net unit-testing design-patterns

我对单元测试相当陌生(我实际上正在研究它)

我的目标当然是能够在下面的类中测试方法。

该类只是检查输入是否已经在缓存中,如果输入不在缓存中,它将返回输入的反转形式(虽然这里没有实现,但假设它存在,因为目的只是为了测试)。

基本上,目标是确保测试 if-else。

这是我的类(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;
        }
    }
}

这是我测试的当前代码:

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 动态地将新行为附加到被装饰的对象上。那是装饰者的责任。这就是您应该测试的内容。

因此,让我们为以下组件编写缓存装饰器:

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

创建测试夹具(或 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);
    }
}

要编译此代码,您需要创建 CachingComponent 类,它接受装饰组件。类似的东西(这里有点加速):

public class CachingComponent : IComponent
{
    private IComponent _component;

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

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

现在让我们定义装饰器的预期行为。它应该将调用传递给组件,如果这是第一次使用一些参数调用:

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

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

它失败了,因此方法还没有实现。第一个实现(好吧,最简单的实现将返回 null,但我们在这里进展得更快):

    public string DoSomething(string value)
    {
        return _component.DoSomething(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());
}

和实现:

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

现在您拥有了经过验证的缓存装饰器。

关于C# 如何正确地对遵循装饰器模式的类进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15367568/

相关文章:

c# - 如何模拟返回 Task<IEnumerable<T>> 和 Task<List<T>> 的方法?

c# - LINQ to SQL 可以生成包含 ISNULL 函数的 TSQL 吗?

c# - 中高级 LINQ/ Entity Framework 查询问题。标记项目

c# - Func<T>.BeginInvoke 是否使用线程池?

c# - 在 C# 中,我可以在编译类型中检查一个类是否可序列化吗?

unit-testing - 使用单元测试项目中定义的启动类时,.NET Core TestServer返回404

C# 到 C#,方便的语言特性

.net - 垃圾收集发生在进程级别还是应用程序域级别?

c# - 为什么.net framework 4.5安装后没有像之前版本一样更新注册表?

c# - 生成唯一密码