Search code examples
c#asp.netasp.net-web-apinunitrhino-mocks

NUnit Test method with Rhino mocks does not work - C#


I have created a web api project and implemented the below HTTP POST method in AccountController and the related service method & repository method in AccountService & AccountRepository respectively.

// WEB API 
public class AccountController : ApiController
{
    private readonly IAccountService _accountService;
    public AccountController()
    {
        _accountService = new AccountService();
    }

    [HttpPost, ActionName("updateProfile")]
    public IHttpActionResult updateProfile([FromBody]RequestDataModel request)
    {
        var response = _accountService.UpdateProfile(request.UserId, request.Salary);
        return Json(response);
    }
}


public class RequestDataModel
{
    public int UserId { get; set; }
    public decimal Salary { get; set; }
}

// Service / Business Layer

public interface IAccountService
{
    int UpdateProfile(int userId, decimal salary);
}

public class AccountService : IAccountService
{
    readonly IAccountRepository _accountRepository = new AccountRepository();

    public int UpdateProfile(int userId, decimal salary)
    {
        return _accountRepository.UpdateProfile(userId, salary);
    }
}


// Repository / Data Access Layer

public interface IAccountRepository
{
    int UpdateProfile(int userId, decimal salary);
}

public class AccountRepository : IAccountRepository
{
    public int UpdateProfile(int userId, decimal salary)
    {
        using (var db = new AccountEntities())
        {
            var account = (from b in db.UserAccounts where b.UserID == userId select b).FirstOrDefault();
            if (account != null)
            {
                account.Salary = account.Salary + salary;
                db.SaveChanges();
                return account.Salary;
            }
        }
        return 0;
    }
}

Also, I wanted to implement a NUNIT test case. Here is the code.

public class TestMethods
{
    private IAccountService _accountService;
    private MockRepository _mockRepository;

    [SetUp]
    public void initialize()
    {
        _mockRepository = new MockRepository();

    }

    [Test]
    public void TestMyMethod()
    {
        var service = _mockRepository.DynamicMock<IAccountService>();

        using (_mockRepository.Playback())
        {
            var updatedSalary = service.UpdateProfile(123, 1000);
            Assert.AreEqual(1000, updatedSalary);
        } 
    }
}

Note that I have used Rhino mocks library to implement the mock repository.

The issue is this does not return the expected output. Looks like it does not trigger the UpdateProfile() method in my service class. it returns NULL.


Solution

  • All of these classes are tightly coupled to implementation concerns and should be refactored to be decoupled and dependent on abstractions.

    public class AccountController : ApiController  {
        private readonly IAccountService accountService;
    
        public AccountController(IAccountService accountService) {
            this.accountService = accountService;
        }
    
        [HttpPost, ActionName("updateProfile")]
        public IHttpActionResult updateProfile([FromBody]RequestDataModel request) {
            var response = accountService.UpdateProfile(request.UserId, request.Salary);
            return Ok(response);
        }
    }
    
    public class AccountService : IAccountService {
        private readonly IAccountRepository accountRepository;
    
        public AccountService(IAccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
        public int UpdateProfile(int userId, decimal salary) {
            return accountRepository.UpdateProfile(userId, salary);
        }
    }
    

    Now for unit testing in isolation the abstract dependencies can be mocked and injected into the subject under test.

    For example the following tests the AccountService.UpdateProfile by mocking a IAccountRepository and injecting it into the AccountService.

    public class AccountServiceTests {
    
        [Test]
        public void UpdateProfile_Should_Return_Salary() {
            //Arrange
            var accountRepository = MockRepository.GenerateMock<IAccountRepository>(); 
            var service = new AccountService(accountRepository);
    
            var userId = 123;
            decimal salary = 1000M;
            var expected = 1000;
    
            accountRepository.Expect(_ => _.UpdateProfile(userId, salary)).Return(expected);
    
            //Act
            var updatedSalary = service.UpdateProfile(userId, salary);
    
            //Assert
            Assert.AreEqual(expected, updatedSalary);
        }
    }
    

    The same approach can be taken for testing the AccountController. Instead you would mock the IAccountService and inject that into the controller to test the action and assert the expected behavior.

    Make sure to register the abstractions and their implementations with the DI container in the composition root of the application.