0

I'd like to test my logic which expects three different interfaces. How can I unite these, because I can't use mock methods while I have three instances. I guess I did something very wrong with the repositories but I have no idea. Thank you for your help!

    [Test]
    public void TestThatLogicABCWorks()
    {
        Mock<IRepositoryA> mockInstance1 = new Mock<IRepositoryA>();
        Mock<IRepositoryB> mockInstance2 = new Mock<IRepositoryB>();
        Mock<IRepositoryC> mockInstance3 = new Mock<IRepositoryC>();
        LogicABC logic = new LogicABC(mockInstance1.Object, mockInstance2.Object, mockInstance3.Object);
    }

Edit: i have three entity classes, 1 general repository and three entity specific repos. In logic I make queries including all three entities, which I reach as:

public class LogicABC : ILogic
{

    IRepository<A> ARepo; //generic repo
    IRepository<B> BRepo;
    IRepository<C> CRepo;

    public LogicABC(IARepository ARepo, IBRepository BRepo, ICRepository CRepo)
    {
        this.ARepo = ARepo; //entity specific repos
        this.BRepo = BRepo;
        this.CRepo = CRepo;
    }

    public LogicABC()
    {
        var Entity = new ABCEntities(); //context
        this.ARepo = new ARepository(Entity);
        this.BRepo = new BRepository(Entity);
        this.CRepo = new CRepository(Entity);
    }
    //queries
    public List<int> Query1()
    {
    var q1 = from x in CRepo.GetAll()
             select x.Id;
    return q1.ToList();
    }

I need to test these queries with mock. For example setup that logicABC.Query1() returns 1 and then verify it.

user9630194
  • 388
  • 2
  • 10
  • 1
    What do you mean `I can't use mock methods while I have three instances`? Why you can't mock methods? Just `mock.Setup(foo => foo.Do()).Returns(() => 1);` – mtkachenko Nov 11 '19 at 17:31
  • Because my mockinstances do not see the method. It only exists in logicABC. My problem that I can't Setup logic, only the interfaces. – user9630194 Nov 11 '19 at 18:18
  • @user9630194 Without a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) that clarifies your specific problem or additional details to highlight exactly what you need, it’s hard to tell exactly what you're asking. – Nkosi Nov 11 '19 at 18:22
  • @user9630194 `It only exists in logicABC. My problem that I can't Setup logic, only the interfaces` It's still not clear. Show full example. – mtkachenko Nov 11 '19 at 18:43
  • I edited it if you can take a look, I'd really appreciate it – user9630194 Nov 11 '19 at 18:55
  • 1
    @user9630194 `Query1()` will not compile since `q1` is not an `int` but a `IQueriable`. So again we do not have a complete picture of what it is you are actually trying to do. – Nkosi Nov 11 '19 at 19:44
  • Does it really matter? I fixed it. – user9630194 Nov 11 '19 at 19:45
  • 1
    Yes it matters because that determines what needs to be mocked for this specific issue. – Nkosi Nov 11 '19 at 20:07
  • This task does not really make sense. It is only for creating a mock (for the sake of it) not for creating a meaningful test. The main point here to mock something with the repositories which does not exist in them. Why does it matter what that something is? I just want to call mockinstancelogic.Setup/Verify – user9630194 Nov 11 '19 at 20:13

2 Answers2

2

When testing a subject under test that has dependencies, the dependencies explicitly needed for the test to flow to complete should be provided.

In this case Query1() is only using ICRepository and has not shown any interaction with the other two dependencies. If that is truly the case then those other dependencies are not needed.

Based on the method under test the following assumptions are being made

public interface ICRepository : IRepository<C> {
    //...
}

and something similar for the other repositories IARepository and IBRepository.

public interface IRemository<TEntity> {
    IEnumerable<TEntity> GetAll();

    //...other members
}

public class C {
    public int Id { get; set; }

    //...other members
}

So the test for Query1 could be done as follows.

[Test]
public void LogicABC_Query1_Should_Return_One_Id() {
    //Arrange
    int expected = 123456789;
    var items = new [] { new C { Id = expectedId } }

    Mock<IRepositoryC> mock = new Mock<IRepositoryC>();
    mock.Setup(_ => _.GetAll()).Returns(items);

    LogicABC logic = new LogicABC(null, null, mock.Object);

    //Act
    List<int> list = logic.Query1();

    //Assert

    mock.Verify(_ => _.GetAll(), Times.Once());

    //checking expected behavior - using FluentAssertions
    list.Should().HaveCount(1);

    int actual = list.First();

    actual.Should().Be(expected); // using FluentAssertions
}

The unit test above tests the expected behavior of one subject method. The same pattern can be done for other members of the class under test.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thank you very much, the test has just passed, I couldn't be happier. – user9630194 Nov 11 '19 at 20:53
  • 1
    @user9630194 glad to help. I hope you understand why we asked for the necessary information and also what was explained in the answer. – Nkosi Nov 11 '19 at 20:54
  • I hate to bother you any longer but this syntax is really strange. Do I understand right that it is still not possible to call verify on logic (for example to decide how many times Query1() was called) with this solution? So it is only good for assert? – user9630194 Nov 11 '19 at 21:32
  • 1
    @user9630194 hold on for update that shows how to check how many times. – Nkosi Nov 11 '19 at 21:34
  • 1
    @user9630194 also take a look at this https://stackoverflow.com/questions/4206193/how-do-i-verify-a-method-was-called-exactly-once-with-moq – Nkosi Nov 11 '19 at 21:35
  • I see...I could have thought about that. Thank you again. – user9630194 Nov 11 '19 at 21:45
0

You need to mock methods in every repository mock, i.e. in mockInstance1, mockInstance2 and mockInstance3. After this you'll be able to verify behavior inside LogicABC by checking applied mocks for every repos

UPDATE:

Your test should look like this:

[Test]
public void TestThatLogicABCWorks()
{
    Mock<IRepositoryA> mockInstance1 = new Mock<IRepositoryA>();
    mockInstance1.Setup(foo => foo.Do()).Returns(() => 1);

    Mock<IRepositoryB> mockInstance2 = new Mock<IRepositoryB>();
    mockInstance2.Setup(boo => boo.Do2()).Returns(() => 2);

    Mock<IRepositoryC> mockInstance3 = new Mock<IRepositoryC>();
    mockInstance3.Setup(doo => doo.Do3()).Returns(() => 3);

    LogicABC logic = new LogicABC(mockInstance1.Object, mockInstance2.Object, mockInstance3.Object);

    logic.DoLogic();

    // do some asserts here
}
Michael Kokorin
  • 474
  • 2
  • 8
  • Thank you for your answer. Can you please explain a little longer what you mean by this? The interfaces do not have the method I'm talking about, so I can't reference it through them. – user9630194 Nov 11 '19 at 18:16
  • 1
    Please look at update above.So when you call logic.DoLogic() - you'll use all your mocks and you'll be able to verify these calls later. – Michael Kokorin Nov 11 '19 at 18:22
  • Thank you again. I may be very stupid here but my interfaces still don't have the method. So what "foo.Do()" is supposed to be exactly? – user9630194 Nov 11 '19 at 18:28
  • @user9630194 you need to show the definition of those dependencies and how they are used withing the subject under test. Without that we cannot provide much solution. We are left guessing. – Nkosi Nov 11 '19 at 18:35
  • "Do" is your interface method which you want to mock. I.e. when you specify mockInstance1.Setup(foo => foo.Do()).Returns(() => 1) - you say that when "Do" method is called on this instance it should return 1. – Michael Kokorin Nov 11 '19 at 18:36