我刚刚开始在我的单元测试中使用AutoFixture.AutoMoq,我发现它对于创建不关心特定值的对象非常有帮助。毕竟,匿名对象的创建就是它的全部。
当我关心一个或多个构造函数参数时,我正在苦苦挣扎。以ExampleComponent为例:
public class ExampleComponent
{
public ExampleComponent(IService service, string someValue)
{
}
}我想编写一个测试,在这个测试中我为someValue提供了一个特定的值,但是让IService由someValue自动创建。
我知道如何在我的IFixture上使用IFixture来保持一个已知的值,这个值将被注入到组件中,但我不太清楚如何提供自己的已知值。
以下是我理想中想要做的事情:
[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>方法:
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的过期日期。
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的有效期编写测试时,我关心的是它的寿命,而不是转换器。
发布于 2013-05-29 17:31:48
你必须替换:
string knownValue = fixture.Freeze<string>("My known value");通过以下方式:
fixture.Inject("My known value");您可以阅读更多关于Inject 这里的内容。
实际上,Freeze扩展方法可以:
var value = fixture.Create<T>();
fixture.Inject(value);
return value;这意味着您在测试中使用的重载实际上称为带有种子的Create<T>:我的已知值导致“我的已知值4d41f94f-1fc9-4115-9f29-e50bc2b4ba5e”。
发布于 2013-06-06 08:28:49
因此,我相信人们可以制定出马克的建议的普遍实施,但我想我应该发表意见。
我创建了一个基于马克的ParameterNameSpecimenBuilder的通用LifeSpanArg
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扩展方法,它设置了定制:
public static class FreezeByNameExtension
{
public static void FreezeByName<T>(this IFixture fixture, string name, T value)
{
fixture.Customizations.Add(new ParameterNameSpecimenBuilder<T>(name, value));
}
}下面的测试现在将通过:
[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; }
}发布于 2013-06-06 06:01:16
你可以这样做。假设您希望为名为TimeSpan的lifespan参数分配一个特定值。
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;
}
}至关重要的是,它可以这样使用:
var fixture = new Fixture();
fixture.Customizations.Add(new LifespanArg(mySpecialLifespanValue));
var sut = fixture.Create<CookieCache>();这种方法可以在某种程度上加以推广,但最后,我们受到缺乏从特定构造函数或方法参数中提取ParameterInfo的强类型方法的限制。
https://stackoverflow.com/questions/16819470
复制相似问题