diff --git a/CHANGELOG.md b/CHANGELOG.md index a5af6a044..791c20fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). +## Unreleased + +#### Fixed + +* Regression: Property stubs not working on sub mock (@aaronburro, #1240) + + ## 4.17.1 (2022-02-26) #### Added diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 891439b0d..5aff75cc5 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -609,6 +609,30 @@ internal static SequenceSetup SetupSequence(Mock mock, LambdaExpression expressi }); } + internal static StubbedPropertySetup SetupProperty(Mock mock, LambdaExpression expression, object initialValue) + { + Guard.NotNull(expression, nameof(expression)); + + var pi = expression.ToPropertyInfo(); + + if (!pi.CanRead(out var getter)) + { + Guard.CanRead(pi); + } + + if (!pi.CanWrite(out var setter)) + { + Guard.CanWrite(pi); + } + + return Mock.SetupRecursive(mock, expression, (targetMock, _, _) => + { + var setup = new StubbedPropertySetup(targetMock, expression, getter, setter, initialValue); + targetMock.MutableSetups.Add(setup); + return setup; + }); + } + private static TSetup SetupRecursive(Mock mock, LambdaExpression expression, Func setupLast, bool allowNonOverridableLastProperty = false) where TSetup : ISetup { diff --git a/src/Moq/Mock`1.cs b/src/Moq/Mock`1.cs index c0f0bb6d6..e4dbf9d70 100644 --- a/src/Moq/Mock`1.cs +++ b/src/Moq/Mock`1.cs @@ -628,22 +628,7 @@ public Mock SetupProperty(Expression> property) /// public Mock SetupProperty(Expression> property, TProperty initialValue) { - Guard.NotNull(property, nameof(property)); - - var pi = property.ToPropertyInfo(); - - if (!pi.CanRead(out var getter)) - { - Guard.CanRead(pi); - } - - if (!pi.CanWrite(out var setter)) - { - Guard.CanWrite(pi); - } - - var setup = new StubbedPropertySetup(this, property, getter, setter, initialValue); - this.MutableSetups.Add(setup); + Mock.SetupProperty(this, property, initialValue); return this; } diff --git a/tests/Moq.Tests/Regressions/IssueReportsFixture.cs b/tests/Moq.Tests/Regressions/IssueReportsFixture.cs index 06b16e717..5c932a318 100644 --- a/tests/Moq.Tests/Regressions/IssueReportsFixture.cs +++ b/tests/Moq.Tests/Regressions/IssueReportsFixture.cs @@ -3740,6 +3740,48 @@ public void Pass_byte_array_indirectly_via_method_call() #endregion + #region 1240 + + public class Issue1240 + { + public interface IFoo { IBar Bar { get; } } + public interface IBar + { + string Prop1 { get; } + string Prop2 { get; set; } + } + + [Fact] + public void Property_on_submock_should_be_stubbed_1() + { + const string prop2 = "Prop2"; + var mock = new Mock(); + + // mock.SetupGet(m => m.Bar.Prop1).Returns("Prop1"); + // ^ This line being commented out is the only difference from the test below. + // Its absence would cause a `NullReferenceException` later. + + mock.SetupProperty(m => m.Bar.Prop2); + mock.Object.Bar.Prop2 = prop2; + Assert.Equal(prop2, mock.Object.Bar.Prop2); + } + + [Fact] + public void Property_on_submock_should_be_stubbed_2() + { + const string prop2 = "Prop2"; + var mock = new Mock(); + + mock.SetupGet(m => m.Bar.Prop1).Returns("Prop1"); + + mock.SetupProperty(m => m.Bar.Prop2); + mock.Object.Bar.Prop2 = prop2; + Assert.Equal(prop2, mock.Object.Bar.Prop2); + } + } + + #endregion + // Old @ Google Code #region #47