2012-02-18 20 views
6

tôi gặp phải một lớp học trong công việc của tôi trông như thế này:Tạo lai của mô hình và đối tượng ẩn danh bằng cách sử dụng ví dụ: Moq và AutoFixture?

public class MyObject 
{ 
    public int? A {get; set;} 
    public int? B {get; set;} 
    public int? C {get; set;} 
    public virtual int? GetSomeValue() 
    { 
    //simplified behavior: 
    return A ?? B ?? C; 
    } 
} 

Vấn đề là tôi có một số mã truy cập A, B và C và gọi phương thức GetSomeValue() (bây giờ, tôi muốn nói đây không phải là một thiết kế tốt, nhưng đôi khi tay tôi bị trói ;-)). Tôi muốn tạo ra một mô hình của đối tượng này, trong đó, cùng một lúc, có A, B và C được đặt thành một số giá trị. Vì vậy, khi tôi sử dụng moq như vậy:

var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock }; 

phép tôi thiết lập một kết quả trên GetSomeValue() phương pháp, nhưng tất cả các thuộc tính được thiết lập để null (và thiết lập tất cả trong số họ sử dụng Cài đặt() là khá cồng kềnh, vì đối tượng thực là một đối tượng dữ liệu khó chịu và có nhiều thuộc tính hơn trong ví dụ đơn giản ở trên).

Vì vậy, mặt khác, sử dụng AutoFixture như thế này:

var fixture = new Fixture(); 
var anyMyObject = fixture.CreateAnonymous<MyObject>(); 

Lá tôi không có khả năng stup một cuộc gọi đến GetSomeValue() phương pháp.

Có cách nào để kết hợp cả hai, để có giá trị ẩn danh và khả năng thiết lập kết quả cuộc gọi không?

Sửa

Dựa trên câu trả lời nemesv, tôi có nguồn gốc phương pháp hữu ích sau đây (hy vọng tôi đã nhận nó đúng):

public static Mock<T> AnonymousMock<T>() where T : class 
{ 
    var mock = new Mock<T>(); 
    fixture.Customize<T>(c => c.FromFactory(() => mock.Object)); 
    fixture.CreateAnonymous<T>(); 
    fixture.Customizations.RemoveAt(0); 
    return mock; 
} 

Trả lời

5

Điều này thực sự có thể thực hiện với Tự động sửa lỗi, nhưng nó yêu cầu một chút tinh chỉnh. Các điểm mở rộng là tất cả ở đó, nhưng tôi thừa nhận rằng trong trường hợp này, giải pháp không phải là đặc biệt có thể phát hiện được.

Sẽ trở nên khó khăn hơn nếu bạn muốn nó hoạt động với các kiểu lồng nhau/phức tạp.

Với lớp MyObject trên, cũng như MyParent lớp này:

public class MyParent 
{ 
    public MyObject Object { get; set; } 

    public string Text { get; set; } 
} 

các đơn vị kiểm tra tất cả các đường chuyền:

public class Scenario 
{ 
    [Fact] 
    public void CreateMyObject() 
    { 
     var fixture = new Fixture().Customize(new MockHybridCustomization()); 

     var actual = fixture.CreateAnonymous<MyObject>(); 

     Assert.NotNull(actual.A); 
     Assert.NotNull(actual.B); 
     Assert.NotNull(actual.C); 
    } 

    [Fact] 
    public void MyObjectIsMock() 
    { 
     var fixture = new Fixture().Customize(new MockHybridCustomization()); 

     var actual = fixture.CreateAnonymous<MyObject>(); 

     Assert.NotNull(Mock.Get(actual)); 
    } 

    [Fact] 
    public void CreateMyParent() 
    { 
     var fixture = new Fixture().Customize(new MockHybridCustomization()); 

     var actual = fixture.CreateAnonymous<MyParent>(); 

     Assert.NotNull(actual.Object); 
     Assert.NotNull(actual.Text); 
     Assert.NotNull(Mock.Get(actual.Object)); 
    } 

    [Fact] 
    public void MyParentIsMock() 
    { 
     var fixture = new Fixture().Customize(new MockHybridCustomization()); 

     var actual = fixture.CreateAnonymous<MyParent>(); 

     Assert.NotNull(Mock.Get(actual)); 
    } 
} 

Có gì trong MockHybridCustomization? Đây:

public class MockHybridCustomization : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customizations.Add(
      new MockPostprocessor(
       new MethodInvoker(
        new MockConstructorQuery()))); 
     fixture.Customizations.Add(
      new Postprocessor(
       new MockRelay(t => 
        t == typeof(MyObject) || t == typeof(MyParent)), 
       new AutoExceptMoqPropertiesCommand().Execute, 
       new AnyTypeSpecification())); 
    } 
} 

Các MockPostprocessor, MockConstructorQueryMockRelay lớp được định nghĩa trong AutoMoq extension để AutoFixture, vì vậy bạn sẽ cần phải thêm một tham chiếu đến thư viện này. Tuy nhiên, lưu ý rằng không bắt buộc phải thêm AutoMoqCustomization.

Lớp AutoExceptMoqPropertiesCommand cũng tùy chỉnh dành cho dịp này:

public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object> 
{ 
    public AutoExceptMoqPropertiesCommand() 
     : base(new NoInterceptorsSpecification()) 
    { 
    } 

    protected override Type GetSpecimenType(object specimen) 
    { 
     return specimen.GetType(); 
    } 

    private class NoInterceptorsSpecification : IRequestSpecification 
    { 
     public bool IsSatisfiedBy(object request) 
     { 
      var fi = request as FieldInfo; 
      if (fi != null) 
      { 
       if (fi.Name == "__interceptors") 
        return false; 
      } 

      return true; 
     } 
    } 
} 

Giải pháp này cung cấp một giải pháp chung cho câu hỏi. Tuy nhiên, nó đã không được thử nghiệm rộng rãi, vì vậy tôi rất muốn nhận được phản hồi về nó.

+0

Đây là một giải pháp tốt, đặc biệt vì nó bao gồm các loại lồng nhau, cho nó chung chung với các loại lồng nhau, có cách nào để loại bỏ phần: t == typeof (MyObject) | | t == typeof (MyParent)? Logic tôi đang nghĩ đến là một cái gì đó như: "nếu loại lồng nhau của lai là mockable, làm cho nó một lai, khác làm cho nó một giá trị vô danh". –

+0

Có, chỉ cần thay thế vị từ bằng một cái gì đó tổng quát hơn. –

+0

Cảm ơn! Tôi cố gắng sử dụng một vị từ của (t! = Null &&! T.IsPrimitive && (t.GetConstructors (BindingFlags.Public) .Length! = 0 || t.GetConstructor (Type.EmptyTypes)! = Null)) và có vẻ như làm việc! Bạn có thấy cái gì thiếu ở đây không? Tôi sẽ cố gắng thử nghiệm một chút và cố gắng cho bạn biết nếu có bất cứ điều gì xảy ra. –

4

Có lẽ có một tốt hơn lý do tại sao, nhưng hoạt động này:

var fixture = new Fixture(); 
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock }; 
moq.Setup(m => m.GetSomeValue()).Returns(3); 

fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object)); 

var anyMyObject = fixture.CreateAnonymous<MyObject>(); 

Assert.AreEqual(3, anyMyObject.GetSomeValue()); 
Assert.IsNotNull(anyMyObject.A); 
//... 

Ban đầu tôi đã cố gắng sử dụng fixture.Register(() => moq.Object); thay vì fixture.Customize nhưng nó đăng ký crea chức năng tor với OmitAutoProperties() vì vậy nó sẽ không hoạt động cho bạn.

2

Kể từ 3.20.0, bạn có thể sử dụng AutoConfiguredMoqCustomization. Điều này sẽ tự động cấu hình tất cả các mocks để các giá trị trả về của các thành viên của chúng được tạo ra bởi AutoFixture.

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

var mock = fixture.Create<Mock<MyObject>>(); 

Assert.NotNull(mock.Object.A); 
Assert.NotNull(mock.Object.B); 
Assert.NotNull(mock.Object.C); 
Các vấn đề liên quan