moq icon indicating copy to clipboard operation
moq copied to clipboard

"Invalid Callback" in NonVoidSetupPhrase.Returns -> ValidateCallbackReturnType is not informative or actionable

Open Prinsn opened this issue 1 year ago • 2 comments

.NET version: .NET 6 Test Context: XUnit Moq: 4.8.12

Summary: Following these examples SO: https://stackoverflow.com/questions/45558470/how-do-i-go-about-unit-testing-with-entity-framework-and-moq Doc: https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking

'Invalid callback. Setup on method with return type 'EntityEntry<Foo>' cannot invoke callback with return type 'Foo'.'
   at Moq.MethodCall.ValidateCallbackReturnType(MethodInfo callbackMethod, Type expectedReturnType)
   at Moq.MethodCall.<>c__DisplayClass23_0.<SetReturnComputedValueBehavior>g__ValidateCallback|4(Delegate callback)
   at Moq.MethodCall.SetReturnComputedValueBehavior(Delegate valueFactory)
   at Moq.Language.Flow.NonVoidSetupPhrase`2.Returns(Delegate valueFunction)
   at <test>

Is thrown immediately on execution, with the message

Setup on method with return type 'EntityEntry<Foo>' cannot invoke callback with return type 'Foo'

Expected: Label, name, method, property, field, class, or any other source of the offending issue.

Actual: Exception Is devoid of any actionable or useful information, as there is no indication or reference to EntityEntry<T> or EntityEntry<Foo in this context.

Details:

        [Fact]
        public void foo()
        {
            //arrange
            var mockFf = new MockDbSet<Foo>(); //Throws immediately on construction
         }

with


    public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class
    {
        public MockDbSet(List<TEntity> dataSource = null)
        {
            var data = (dataSource ?? new List<TEntity>());
            var queryable = data.AsQueryable();

            this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
            this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
            this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
            this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());
            
            //Mocking the insertion of entities
            this.Setup(_ => _.Add(It.IsAny<TEntity>())).Returns((TEntity arg) => {
                data.Add(arg);
                return arg;
            });

            //...the same can be done for other members like Remove
        

      } //Debug call stack is here
    }

and Foo as

   public class Foo
   {
       [Key]
       public int Id { get; set; }

       public DateTime CreatedDate { get; set; }

       [MaxLength(36), Required]
       public string CreatedByUserId { get; set; }
       public Employee CreatedByUser { get; set; }

       public DateTime? ModifiedDate { get; set; }

       [MaxLength(36)]
       public string ModifiedByUserId { get; set; }
       public Employee ModifiedByUser { get; set; }
   
       [Required]
       public string Name { get; set; }

       [MaxLength(10)]
       public string ReferenceNumber { get; set; }

       public int? BazId { get; set; }
       public Baz Baz { get; set; }

       public ICollection<Bar> Bars{ get; set; }
   }

Prinsn avatar Nov 14 '22 17:11 Prinsn