0

I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.

I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.

namespace ChatProj.Service_Layer
{
    public class UserService : IUserService
    {
        public MessageContext messageContext = new MessageContext();

        public UserService()
        {
            _messageRepository = new MessageRepository(messageContext);
        }

        private  IMessageRepository _messageRepository;

    ->  public void SaveMessage(Message message)
        {
             messageContext.Messages.Add(message);
            _messageRepository.Save();
        }

The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:

public void Save()
        {
            context.SaveChanges();
        }

This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).

Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:

namespace ChatProj.Tests
{
    [TestFixture]
    class MessageRepositoryTests
    {
        [SetUp]
        public void Setup()
        {

        }

        [Test]
    public void SaveMessage_SaveWorking_VerifyUse()
    {
        //Arrange
        var userServiceMock = new Mock<UserService>();
        var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
        var repositoryMock = new Mock<IMessageRepository>();
        var contextMock = new Mock<MessageContext>();
        MessageRepository messageRepository = new MessageRepository(contextMock.Object);
        UserService userService = new UserService();

        //Act
        userService.SaveMessage(message);

        //Assert
        repositoryMock.Verify(m => m.Save());
        userServiceMock.Verify(m => m.SaveMessage(message));
    }
}

I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.

So I'm wondering, how do I practically get my Unit Test to work?

2 Answers 2

2

You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method. Right now it still tries to access a real DB.

But you can setup only virtual properties or if it will be an inteface.

So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.

Sign up to request clarification or add additional context in comments.

21 Comments

Yes, you should add it to your MessageRepository ctor. DON'T use DbSet<T>! Change it to IDbSet<T>. In order to return in-memory data you have two options 1) use some mocking framework like Moq or NSubstitute 2) create your own InMemoryMessageContext implemetation of IMessageContext interface and InMemoryDbSet<T> implementation of IDbSet<T> interface. Setup everything together and fill InMemoryDbSet<T> with data.
You need to make public class MessageContext : IMessageContext and remove MessageContext context from your MessageRepository ctor parameters. Leave just interface there. And pass new MessageContext() in real code and mock in unit test.
You should add SaveChanges method to your IMessageContext (and also all other properties\methods of MessageContext that you are using in controller) and declare private IMessageContext context. You should also change DbSet<Message> into IDbSet<Message> in your MessageContext class.
Just add int SaveChanges(); in your IMessageContext definition. That's all. Since DbContext already implements this method you have not to change anything else;
You are on the right way. Just add IMessageContext to your MessageContext like public class MessageContext : DbContext, IMessageContext. And everything will be fine :).
|
1

Take a look at these two lines:

UserService userService = new UserService();

//Act
userService.SaveMessage(message);

You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.

public void SaveMessage(Message message)
{
     messageContext.Messages.Add(message);
    _messageRepository.Save();
}

Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?

public MessageContext messageContext = new MessageContext();

public UserService()
{
    _messageRepository = new MessageRepository(messageContext);
}

You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.