2012-04-25 106 views
2

类AutoFixture.CreateAnonymous <>()我已经做了类似下面的如何在内部构造函数

public class Foo 
{ 
    public Bar { get; set; } 
} 

public class Bar 
{ 
    internal Bar(string id) 
    {} 
} 

public static class Bars 
{ 
    public static class TypeOne 
    { 
      public static readonly SimpleBar = new Bar("id-for-type1-simple-bar"); 
    } 
} 

现在,我在单元测试组件添加了InternalsVisibleTo我要能访问内部酒吧构造函数,然后我做:

var fixture = new Fixture(); 
fixture.Register(() => new Bar(fixture.CreateAnonymous<string>())); 
var foo = fixture.CreateAnonymous<Foo>(); 

问:
是“正确”的方式来利用AutoFixture或者是有一个更好的?

+3

“正确”的方法是只测试被测系统的公共API。因此,InternalsVisibleTo充其量只是治疗症状而不是疾病。你不能让构造函数公开吗? – 2012-04-25 16:56:25

+0

@MarkSeemann:我可以,但'Bar'实例的所有可能值都列在'static class Bars'中 - 因此不需要创建其他实例... – Nils 2012-04-25 18:07:40

回答

2

难道你不能只是做这样的事情呢?

fixture.Inject(Bars.TypeOne.SimpleBar); 

这是全部公开的。无需InternalsVisibleTo,我想......

+0

+1根据您的评论,最明智的做法是唯一允许的值由“酒吧”已知# – 2012-04-26 07:33:37

2

此代码的工作,因为你已经有一个公共静态类引用吧,这个工作没有设置InternalsVisibleTo属性

 var fixture = new Fixture().Customize(new AutoMoqCustomization()); 

     fixture.Inject(Bars.TypeOne.SimpleBar); 

     var sut = fixture.CreateAnonymous<Foo>(); 

     sut.Should().NotBeNull(); 
     sut.Bar.Should().NotBeNull(); 
     sut.Bar.Should().Be(Bars.TypeOne.SimpleBar); 
2

永远不要往下走的路InternalsVisibleTo - 不要让我开始列表,但它始于系统的每个部分,包括所有强名单的测试助手。

如果你绝对不能这么想 - 大多数情况下你最好把内部部件放在内部,你不会考虑把你的公共API放到命名空间中,这样它们就不会混淆任何东西 - 例如Xunit.Sdk和我为雇主使用的各种子命名空间),然后正确的方式来管理这些暴露的东西。仅供测试使用的是For Tests Only的概念。注意这个名字的原因在于它被正确地称为the xUnit Test Patterns book中的反模式。


现在回答你的问题......

  1. 考虑隐藏的东西,用户一般不应该需要一个单独的命名空间
  2. 不要使用InternalsVisibleTo
  3. 不使用只测试代码

公开一个只在内部暴露的工厂(可能是?)构建软件的(但最好你有它在另一个命名空间无条件):

#if INCLUDE_FOR_TEST_ONLY 
public static class BarsForTestOnly 
{ 
    public static Bar Create(string id) 
    { 
     return new Bar(id); 
    } 
} 
#endif 

而且使用这样的:

#if INCLUDE_FOR_TEST_ONLY 
public class Facts 
{ 

[Fact] 
public void Fact() 
{ 
    Fixture fixture = new Fixture(); 
    fixture.Register((string name) => BarsForTestOnly.Create(name)); 
    var anonymousBar = fixture.CreateAnonymous<Bar>(); 
} 

[Fact] 
public void FactSyntax2() // Just a variant of Fact above 
{ 
    Fixture fixture = new Fixture(); 
    fixture.Register<string, Bar>(BarsForTestOnly.Create); 
    var anonymousBar = fixture.CreateAnonymous<Bar>(); 
} 

[Fact] 
public void UsingFromFactory() 
{ 
    Fixture fixture = new Fixture(); 
    fixture.Customize<Bar>( x=> x.FromFactory<string>(BarsForTestOnly.Create)); 
    var anonymousBar = fixture.CreateAnonymous<Bar>(); 
} 
#endif 

或者,使你有Just Work,你可以这样做:

[Fact] 
public void UsingCtor() 
{ 
    Fixture fixture = new Fixture(); 
    fixture.Register((string name)=> new Bar(name)); 
    var anonymousBar = fixture.CreateAnonymous<Bar>(); 
} 
+1

+1在命名空间中“隐藏”事物的另一种替代方法是将接口用作一种访问修饰符本身:http://blog.ploeh.dk/2011/02/28/InterfacesAreAccessModifiers.aspx – 2012-04-26 10:57:50

+0

@Mark Seemann:已阅读并重视该文章中的见解,但这里并没有想到它。定义一个在这个空间中考虑的技术。 (尽管显然在这个特定的情况下,它(仅在一个明确实现的接口上的方法)只有在你已经拥有了一个对象的实例后才变得有价值,这是OP后面的鸡)。 – 2012-04-26 12:16:05