首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AutoFixture.AutoMoq为一个构造函数参数提供一个已知值。

AutoFixture.AutoMoq为一个构造函数参数提供一个已知值。
EN

Stack Overflow用户
提问于 2013-05-29 16:37:04
回答 6查看 14.8K关注 0票数 31

我刚刚开始在我的单元测试中使用AutoFixture.AutoMoq,我发现它对于创建不关心特定值的对象非常有帮助。毕竟,匿名对象的创建就是它的全部。

当我关心一个或多个构造函数参数时,我正在苦苦挣扎。以ExampleComponent为例:

代码语言:javascript
复制
public class ExampleComponent
{
    public ExampleComponent(IService service, string someValue)
    {
    }
}

我想编写一个测试,在这个测试中我为someValue提供了一个特定的值,但是让IServicesomeValue自动创建。

我知道如何在我的IFixture上使用IFixture来保持一个已知的值,这个值将被注入到组件中,但我不太清楚如何提供自己的已知值。

以下是我理想中想要做的事情:

代码语言:javascript
复制
[TestMethod]
public void Create_ExampleComponent_With_Known_SomeValue()
{
    // create a fixture that supports automocking
    IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());

    // supply a known value for someValue (this method doesn't exist)
    string knownValue = fixture.Freeze<string>("My known value");

    // create an ExampleComponent with my known value injected 
    // but without bothering about the IService parameter
    ExampleComponent component = this.fixture.Create<ExampleComponent>();

    // exercise component knowning it has my known value injected
    ...
}

我知道我可以通过直接调用构造函数来做到这一点,但这将不再是匿名对象创建。是否有一种像这样使用AutoFixture.AutoMock的方法,或者我是否需要将DI容器合并到我的测试中,以便能够做我想做的事情?

编辑:

在我最初的问题中,我可能应该少缺席一些,所以这里是我的具体情况。

我有一个ICache接口,它具有通用的TryRead<T>Write<T>方法:

代码语言:javascript
复制
public interface ICache
{
    bool TryRead<T>(string key, out T value);

    void Write<T>(string key, T value);

    // other methods not shown...  
}

我正在实现一个CookieCache,其中ITypeConverter处理与字符串之间的对象转换,lifespan用于设置cookie的过期日期。

代码语言:javascript
复制
public class CookieCache : ICache
{
    public CookieCache(ITypeConverter converter, TimeSpan lifespan)
    {
        // usual storing of parameters
    }

    public bool TryRead<T>(string key, out T result)
    {
        // read the cookie value as string and convert it to the target type
    }

    public void Write<T>(string key, T value)
    {
        // write the value to a cookie, converted to a string

        // set the expiry date of the cookie using the lifespan
    }

    // other methods not shown...
}

因此,当我为cookie的有效期编写测试时,我关心的是它的寿命,而不是转换器。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-05-29 17:31:48

你必须替换:

代码语言:javascript
复制
string knownValue = fixture.Freeze<string>("My known value");

通过以下方式:

代码语言:javascript
复制
fixture.Inject("My known value");

您可以阅读更多关于Inject 这里的内容。

实际上,Freeze扩展方法可以:

代码语言:javascript
复制
var value = fixture.Create<T>();
fixture.Inject(value);
return value;

这意味着您在测试中使用的重载实际上称为带有种子的Create<T>:我的已知值导致“我的已知值4d41f94f-1fc9-4115-9f29-e50bc2b4ba5e”。

票数 21
EN

Stack Overflow用户

发布于 2013-06-06 08:28:49

因此,我相信人们可以制定出马克的建议的普遍实施,但我想我应该发表意见。

我创建了一个基于马克的ParameterNameSpecimenBuilder的通用LifeSpanArg

代码语言:javascript
复制
public class ParameterNameSpecimenBuilder<T> : ISpecimenBuilder
{
    private readonly string name;
    private readonly T value;

    public ParameterNameSpecimenBuilder(string name, T value)
    {
        // we don't want a null name but we might want a null value
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("name");
        }

        this.name = name;
        this.value = value;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
        {
            return new NoSpecimen(request);
        }

        if (pi.ParameterType != typeof(T) ||
            !string.Equals(
                pi.Name, 
                this.name, 
                StringComparison.CurrentCultureIgnoreCase))
        {
            return new NoSpecimen(request);
        }

        return this.value;
    }
}

然后,我在FreezeByName上定义了一个通用的IFixture扩展方法,它设置了定制:

代码语言:javascript
复制
public static class FreezeByNameExtension
{
    public static void FreezeByName<T>(this IFixture fixture, string name, T value)
    {
        fixture.Customizations.Add(new ParameterNameSpecimenBuilder<T>(name, value));
    }
}

下面的测试现在将通过:

代码语言:javascript
复制
[TestMethod]
public void FreezeByName_Sets_Value1_And_Value2_Independently()
{
    //// Arrange
    IFixture arrangeFixture = new Fixture();

    string myValue1 = arrangeFixture.Create<string>();
    string myValue2 = arrangeFixture.Create<string>();

    IFixture sutFixture = new Fixture();
    sutFixture.FreezeByName("value1", myValue1);
    sutFixture.FreezeByName("value2", myValue2);

    //// Act
    TestClass<string> result = sutFixture.Create<TestClass<string>>();

    //// Assert
    Assert.AreEqual(myValue1, result.Value1);
    Assert.AreEqual(myValue2, result.Value2);
}

public class TestClass<T>
{
    public TestClass(T value1, T value2)
    {
        this.Value1 = value1;
        this.Value2 = value2;
    }

    public T Value1 { get; private set; }

    public T Value2 { get; private set; }
}
票数 22
EN

Stack Overflow用户

发布于 2013-06-06 06:01:16

你可以这样做。假设您希望为名为TimeSpanlifespan参数分配一个特定值。

代码语言:javascript
复制
public class LifespanArg : ISpecimenBuilder
{
    private readonly TimeSpan lifespan;

    public LifespanArg(TimeSpan lifespan)
    {
        this.lifespan = lifespan;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
            return new NoSpecimen(request);

        if (pi.ParameterType != typeof(TimeSpan) ||
            pi.Name != "lifespan")   
            return new NoSpecimen(request);

        return this.lifespan;
    }
}

至关重要的是,它可以这样使用:

代码语言:javascript
复制
var fixture = new Fixture();
fixture.Customizations.Add(new LifespanArg(mySpecialLifespanValue));

var sut = fixture.Create<CookieCache>();

这种方法可以在某种程度上加以推广,但最后,我们受到缺乏从特定构造函数或方法参数中提取ParameterInfo的强类型方法的限制。

票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16819470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档