4

I am using the Moq framework in my Unit Test. This is the UpdateApplication test method:

[TestMethod]
public void UpdateApplication()
{
    const string newAplpicationName = "NewApplication1";

    var data =
        new[]
        {
            new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
            new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
        }
            .AsQueryable();

    var mockSet = new Mock<DbSet<Application>>();
    mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
    mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
    mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

    mockSet.Setup(m => m.AddOrUpdate(It.IsAny<Application[]>())).Callback(
        (Application[] apps) =>
        {
            apps.FirstOrDefault(m => m.Id == 1).Name = newAplpicationName;
        }).Verifiable(); // <-- Exception

    var mockContext = new Mock<AppDbContext>();
    mockContext.Setup(c => c.Applications).Returns(mockSet.Object);

    // Act 
    var commandHandler = new UpdateApplicationCommandHandler(mockContext.Object);
    var commandArg = new ApplicationCommandArg { Id = 1, Name = newAplpicationName };
    commandHandler.Execute(new UpdateApplicationCommand(commandArg));

    // Verify
    mockContext.Verify(m => m.SaveChanges(), Times.Once());
}

I got an exception when ran the test:

An exception of type 'System.NotSupportedException' occurred in Moq.dll but was
not handled in user code

Additional information: Expression references a method that does not belong to
the mocked object: m => m.AddOrUpdate(It.IsAny())

   at Moq.Mock.ThrowIfNotMember(Expression setup, MethodInfo method)
   at Moq.Mock.c__DisplayClass19`1.b__18()
   at Moq.PexProtector.Invoke[T](Func`1 function)
   at Moq.Mock.Setup[T](Mock`1 mock, Expression`1 expression, Condition condition)
   at Moq.Mock`1.Setup(Expression`1 expression)
   at UpdateApplication() in UpdateApplicationCommandTests.cs:line 39

How should I write unit tests for update and delete actions using Moq?

Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
Vitone
  • 498
  • 1
  • 7
  • 19

2 Answers2

3

This variant of UpdateApplication unit test method is working for me but I am not sure if it is correct:

    [TestMethod]
    public void UpdateApplication()
    {
        const string newAplpicationName = "NewApplication1";

        var data =
            new[]
            {
                new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
                new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
            }
                .AsQueryable();

        var mockSet = new Mock<DbSet<Application>>();
        mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var mockContext = new Mock<AppDbContext>();
        mockContext.Setup(m => m.Applications).Returns(mockSet.Object);


        // Act 
        var commandHandler = new UpdateApplicationCommandHandler(mockContext.Object);
        var commandArg = new ApplicationCommandArg { Id = 1, Name = newAplpicationName };
        commandHandler.Execute(new UpdateApplicationCommand(commandArg));

        Assert.AreEqual(newAplpicationName, data.First(m => m.Id == 1).Name);

        mockContext.Verify(m => m.SaveChanges(), Times.Once());
    }

But I still have a problem with my DeleteApplicationCommandTest. When I run the test I get an excepton "Expected invocation on the mock exactly 3 times, but was 2 times: m => m.Applications". This is test method:

    [TestMethod]
    public void DeleteApplication()
    {
        var data =
            new[]
            {
                new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
                new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
            }
                .AsQueryable();

        var mockSet = new Mock<DbSet<Application>>();
        mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var mockContext = new Mock<AppDbContext>();
        mockContext.Setup(m => m.Applications).Returns(mockSet.Object);

        // Act 
        var commandHandler = new DeleteApplicationCommandHandler(mockContext.Object);
        var commandArg = new ApplicationCommandArg { Id = 1 };
        commandHandler.Execute(new DeleteApplicationCommand(commandArg));

        // Verify
        mockSet.Verify(m => m.Remove(It.IsAny<Application>()), Times.Once());
        mockContext.Verify(m => m.SaveChanges(), Times.Once());
        mockContext.VerifyGet(m => m.Applications, Times.Exactly(3));
    }

This is my Execute method of DeleteApplicationCommandHandler:

    public override void Execute(DeleteApplicationCommand command)
    {
        Debug.WriteLine("DeleteApplicationCommand executed");

        var application = this.DbContext.Applications.FirstOrDefault(m => m.Id == command.CommandArg.Id);

        if (application == null)
        {
            throw new Exception(string.Format("Application with id {0} was not found", command.CommandArg.Id));
        }

        this.DbContext.Applications.Remove(application);

        this.DbContext.SaveChanges();
    }

Why DeleteApplication test method fails?

Vitone
  • 498
  • 1
  • 7
  • 19
1

The problem is that AddOrUpdate is an extension method. Moq cannot mock extension methods, so you'll need to find another way to achieve your test coverage.

Community
  • 1
  • 1
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88