简体   繁体   English

如何使用 Mock (Moq) 捕获属性值的设置

[英]How to capture the setting of a property value with Mock (Moq)

In my test project, I want to capture a property set by the SUT to a mocked object.在我的测试项目中,我想将 SUT 设置的属性捕获到模拟对象。 I have tried many things, but none seem to allow me to capture that.我尝试了很多东西,但似乎没有一个能让我捕捉到这一点。

I set up a short example:我设置了一个简短的示例:

The mocked interface:模拟界面:

public interface ISomeInterface
{
    string SomeProperty { get; set; }
}

The SUT: SUT:

public class SomeSystemUnderTest
{
    public void AssignSomeValueToThis(ISomeInterface obj)
    {
        obj.SomeProperty = Guid.NewGuid().ToString();
    }
}

The test:考试:

[TestClass]
public class SomeTests
{
    [TestMethod]
    public void TestSomeSystem()
    {
        // Arrange
        var someInterfaceMock = new Mock<ISomeInterface>();

        someInterfaceMock.SetupSet(m => m.SomeProperty = It.IsAny<string>()).Verifiable();

        // Act
        var sut = new SomeSystemUnderTest();
        sut.AssignSomeValueToThis(someInterfaceMock.Object);

        // Assert
        // HERE I WOULD LIKE TO READ WHAT VALUE WAS ASSIGNED
        string myVal = someInterfaceMock.Object.SomeProperty;
    }
}

The "myVal" variable stays null, and by examining the mock, we can see the property is still null. “myVal”变量保持为空,通过检查模拟,我们可以看到该属性仍然为空。 I did not really expected it to have some value, just trying.我并没有真正期望它有一些价值,只是尝试。

I tried with Setup, with a callback, I get compilation errors.我尝试使用安装程序,通过回调,我得到编译错误。

In real life project, the SUT is to transform a mocked object property to something dependant to another object property.在现实生活项目中,SUT 是将模拟对象属性转换为依赖于另一个对象属性的东西。 To know if the object is doing its job, I need to be able to read the property.要知道对象是否正在完成其工作,我需要能够读取该属性。 Note, I cannot re-designed the mocked interfaces, they are 3rd party.请注意,我无法重新设计模拟接口,它们是第 3 方。

I tried to use VerifySet, but it seems to take a hard-coded value only.我尝试使用 VerifySet,但它似乎只采用硬编码值。

Thank you, Michel谢谢你,米歇尔

There is a difference between get and set and mock actually doesn't have any internal state but only the setups which it tries to match and behave properly. getset之间有区别,mock 实际上没有任何内部状态,只有它试图匹配和正常运行的设置。 You could mimic real get and set functionality by using callback.您可以通过使用回调来模拟真实的getset功能。 Something like this:像这样的东西:

//Arrange
string someProperty = null;
var mock = new Mock<ISomeInterface>();

mock.SetupSet(m => m.SomeProperty = It.IsAny<string>())
    .Callback<string>(p => someProperty = p)
    .Verifiable();

// use func instead of value to defer the resulution to the invocation moment
mock.SetupGet(m => m.SomeProperty).Returns(() => someProperty);

//Act
mock.Object.SomeProperty = "test";

//Assert
Assert.AreEqual("test", mock.Object.SomeProperty);

The other possibility is to use Capture itself it actually exist within moq另一种可能性是使用Capture本身,它实际上存在于moq

//Arrange
List<string> someProperty = new List<string>();
var mock = new Mock<ISomeInterface>();

mock.SetupSet(m => m.SomeProperty = Capture.In(someProperty))
    .Verifiable();

mock.SetupGet(m => m.SomeProperty).Returns(() => someProperty.Last());

//Act
mock.Object.SomeProperty = "test";

//Assert
Assert.AreEqual("test", mock.Object.SomeProperty);

General Answer一般回答

Moq actually has special methods for that: Moq 实际上有特殊的方法:

  • To setup an individual property: mock.SetupProperty(m => m.SomeProperty) , optionally passing a default value要设置单个属性: mock.SetupProperty(m => m.SomeProperty) ,可选择传递默认值
  • To setup all properties: mock.SetupAllProperties()设置所有属性: mock.SetupAllProperties()

Specific Cases具体案例

There are however specific cases where this isn't enough, for example if we want to setup a property and give it a default value without setting the property value via the property setters.然而,在某些特定情况下这还不够,例如,如果我们想设置一个属性并为其赋予一个默认值,而不通过属性设置器设置属性值。

(One example case, is when we need the property with the set value for use in the constructor, in which case we cannot set the property directly as it would require calling .Object which implicitly calls the constructor). (一个例子是,当我们需要在构造函数中使用具有设置值的属性时,在这种情况下我们不能直接设置属性,因为它需要调用隐式调用构造函数的.Object )。

In this situation one should use the answer of @Johnny.在这种情况下,应该使用@Johnny 的答案。

Here is a generic extension method version of his answer:这是他的答案的通用扩展方法版本:

public static void SetupAutoProperty<TObject, TProperty>(this Mock<TObject> mock,
       Expression<Func<TObject, TProperty>> memberAccessExpr, 
       TProperty initialValue) where TObject : class
{
     var propStates = new List<TProperty>();            
     Expression<Action> captureExpression = () => Capture.In(propStates);

     var finalExpression = Expression.Lambda<Action<TObject>>(
                 Expression.Assign(memberAccessExpr.Body, captureExpression.Body),
                                        memberAccessExpr.Parameters);

      mock.SetupSet(finalExpression.Compile());
      mock.SetupGet(memberAccessExpr).Returns(() => 
                 propStates.Any() ? propStates.Last() : initialValue);
}

And call it as in someInterfaceMock.SetupAutoProperty(m => m.SomeProperty, someInitialValue)并将其称为someInterfaceMock.SetupAutoProperty(m => m.SomeProperty, someInitialValue)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM