The correct way to check the parameters passed to the Mock is set as expected

Is it acceptable to accept calls in your callbacks if you later confirm that the methods were called? Is this the preferred way to make sure my layout gets the expected parameters passed to it, or should I set a local variable in my callback and execute the statements in this instance?

I have a situation where I have some kind of logic in the Presenter class that outputs values ​​based on the input data and passes them to the Creator class. To check the logic in the Presenter class, I want to check that the corresponding derived values ​​are observed when the Creator is called. I came up with an example below that works, but I'm not sure if I like this approach:

[TestFixture] public class WidgetCreatorPresenterTester { [Test] public void Properly_Generates_DerivedName() { var widgetCreator = new Mock<IWidgetCreator>(); widgetCreator.Setup(a => a.Create(It.IsAny<Widget>())) .Callback((Widget widget) => Assert.AreEqual("Derived.Name", widget.DerivedName)); var presenter = new WidgetCreatorPresenter(widgetCreator.Object); presenter.Save("Name"); widgetCreator.Verify(a => a.Create(It.IsAny<Widget>()), Times.Once()); } } 

I am worried that without a Verify call at the end, there is no guarantee that the call in the callback will be called. Another approach would be to set a local variable in the callback:

 [Test] public void Properly_Generates_DerivedName() { var widgetCreator = new Mock<IWidgetCreator>(); Widget localWidget = null; widgetCreator.Setup(a => a.Create(It.IsAny<Widget>())) .Callback((Widget widget) => localWidget = widget); var presenter = new WidgetCreatorPresenter(widgetCreator.Object); presenter.Save("Name"); widgetCreator.Verify(a => a.Create(It.IsAny<Widget>()), Times.Once()); Assert.IsNotNull(localWidget); Assert.AreEqual("Derived.Name", localWidget.DerivedName); } 

I feel that this approach is less error prone as it is more explicit and it is easier to see that Assert statements will be called. Is one approach preferable to another? Is there an easier way to check the input parameter passed to the layout that I am missing?

In case this is useful, here is the rest of the code for this example:

 public class Widget { public string Name { get; set; } public string DerivedName { get; set; } } public class WidgetCreatorPresenter { private readonly IWidgetCreator _creator; public WidgetCreatorPresenter(IWidgetCreator creator) { _creator = creator; } public void Save(string name) { _creator.Create( new Widget { Name = name, DerivedName = GetDerivedName(name) }); } //This is the method I want to test private static string GetDerivedName(string name) { return string.Format("Derived.{0}", name); } } public interface IWidgetCreator { void Create(Widget widget); } 

EDIT
I updated the code to make the second approach that I outlined in the question easier to use. I pulled the creation of the expression used in Setup / Verify to a separate variable, so I only need to define it once. I feel that this method is what I like best with, it is easy to configure, and fails with good error messages.

 [Test] public void Properly_Generates_DerivedName() { var widgetCreator = new Mock<IWidgetCreator>(); Widget localWidget = null; Expression<Action<IWidgetCreator>> expressionCreate = (w => w.Create(It.IsAny<Widget>())); widgetCreator.Setup(expressionCreate) .Callback((Widget widget) => localWidget = widget); var presenter = new WidgetCreatorPresenter(widgetCreator.Object); presenter.Save("Name"); widgetCreator.Verify(expressionCreate, Times.Once()); Assert.IsNotNull(localWidget); Assert.AreEqual("Derived.Name", localWidget.DerivedName); } 
+6
source share
4 answers

Due to how your code is structured, you are forced to test two things in one unit test. You test that A) your moderator calls the WidgetCreator input method and B) that the correct name is set on the new widget. If possible, it would be better if you could somehow do these two things with two separate tests, but in this case I really don't see a way to do this.

Given all this, I think the second approach is cleaner. It more explicitly relates to what you expect, and if it fails, it would be completely clear why and where it does not work.

+3
source

I am doing Verify with matches according to AAA. And because of this, installation is not required. You can embed it, but I separated it to make it look cleaner.

 [Test] public void Properly_Generates_DerivedName() { var widgetCreator = new Mock<IWidgetCreator>(); var presenter = new WidgetCreatorPresenter(widgetCreator.Object); presenter.Save("Name"); widgetCreator.Verify(a => a.Create(MatchesWidget("Derived.Name")); } private Widget MatchesWidget(string derivedName) { return It.Is<Widget>(m => m.DerivedName == derivedName); } 
+4
source

Just to detail @rsbarro's comment - Moq failure error message:

The expected call to the layout at least once, but never executed

... less useful for complex types, when determining exactly which condition really failed when searching for errors (whether in code or unit test).

I often come across this when using Moq Verify to check for a large number of conditions in Verify , where the method had to be called with certain parameter values ​​that are not primitives, such as int or string . (This is usually not a problem for primitive types, since Moq lists the actual "executed calls" in the method as part of the exception).

As a result, in this case I will need to capture the passed parameters (which, it seems to me, duplicate the work of Moq ), or simply move the Assertion inline line using Setup / Callbacks .

eg. Verification:

 widgetCreator.Verify(wc => wc.Create( It.Is<Widget>(w => w.DerivedName == "Derived.Name" && w.SomeOtherCondition == true), It.Is<AnotherParam>(ap => ap.AnotherCondition == true), Times.Exactly(1)); 

Will be recoded as

 widgetCreator.Setup(wc => wc.Create(It.IsAny<Widget>(), It.IsAny<AnotherParam>()) .Callback<Widget, AnotherParam>( (w, ap) => { Assert.AreEqual("Derived.Name", w.DerivedName); Assert.IsTrue(w.SomeOtherCondition); Assert.IsTrue(ap.AnotherCondition, "Oops"); }); // *** Act => invoking the method on the CUT goes here // Assert + Verify - cater for rsbarro concern that the Callback might not have happened at all widgetCreator.Verify(wc => wc.Create(It.IsAny<Widget>(), It.Is<AnotherParam>()), Times.Exactly(1)); 

At first glance, this violates AAA , because we put an Assert inline using Arrange (although the callback is called only during Act ), but at least we can get to the bottom of the problem.

Also see Hady's idea of ​​moving the tracking callback lambda to your named function or, even better, to C # 7, this can be moved to the Local function at the bottom of the unit test method, so the AAA layout can be saved.

+3
source

At the top of StuartLC, answer in this thread, you follow what it offers without violating AAA by writing the inline function, which is passed to the Verify method of the breadboard object.

So for example:

 // Arrange widgetCreator .Setup(wc => wc.Create(It.IsAny<Widget>(), It.IsAny<AnotherParam>()); // Act // Invoke action under test here... // Assert Func<Widget, bool> AssertWidget = request => { Assert.AreEqual("Derived.Name", w.DerivedName); Assert.IsTrue(w.SomeOtherCondition); Assert.IsTrue(ap.AnotherCondition, "Oops"); return true; }; widgetCreator .Verify(wc => wc.Create(It.Is<Widget>(w => AssertWidget(w)), It.Is<AnotherParam>()), Times.Exactly(1)); 
+1
source

Source: https://habr.com/ru/post/886190/


All Articles