Andy Orchard
Andy Orchard

Reputation: 15

Writing unit test for business logic in c#

I am currently learning c# and need to write unit tests for a business logic layer written by another dev. The method I need to test is a Get method that retrieves a single object from our db. However I am unsure how to do it as I dont have much experience with c# and cant find any examples online.

Here is the data access method:

    public void CreateUser(string userName, int phoneNumber, int age)
    {
        using MySqlCommand mySqlCommand = new MySqlCommand("usp_CreateUser");
        mySqlCommand.CommandType = CommandType.StoredProcedure;
        mySqlCommand.Parameters.AddWithValue("UserName", userName);
        mySqlCommand.Parameters.AddWithValue("TailLength", iconSize);
        mySqlCommand.Parameters.AddWithValue("IconSize", tailLength);

        ExecuteNonQuery(mySqlCommand);
    }

    public UserSettings UpdateUser(string userName, User user)
    {
        if (GetUser(userName) == null)
            return null;

        using MySqlCommand updateCommand = new("usp_UpdateUserByUserName");
        updateCommand.CommandType = CommandType.StoredProcedure;
        updateCommand.Parameters.AddWithValue("userName", userName);
        updateCommand.Parameters.AddWithValue("phoneNumber", userSetting.PhoneNumber);
        updateCommand.Parameters.AddWithValue("age", userSetting.Age);
        ExecuteNonQuery(updateCommand);
        
        return GetUserSetting(userName);
    }

The actual model for a user:

public class User
{
    public int? PhoneNumber { get; set; }
    public int? Age { get; set; }
}

Now the business logic itself:

    public User GetUser(string userName)
    {
        var userSetting = _userDataAccess.GetUser(userName);
        if(user == null)
        {
            _userDataAccess.CreateUser(userName, AppSettings.GetCurrentSettings().PhoneNumber, AppSettings.GetCurrentSettings().Age);
            user = _userDataAccess.GetUser(userName);
        }
        return user;
    }

    public User UpdateUser(string userName, User user)
    {
        var updatedUser = _userDataAccess.UpdateUser(userName, user);
        if (updatedUser == null)
        {
            throw new HttpNotFoundException("User does not exist");
        }
        return updatedUser;
    }

So I am trying to write tests for both these methods and I'm not sure how to do so. I have tried to do the following:

public class BusinessLogicTests
{
    private Mock<IUserDataAccess> _mockDataAccess;

    private UserBusinessLogic _userBusinessLogic;
    private User user;

    public BusinessLogicTests()
    {
        _mockDataAccess = new Mock<IUserDataAccess>();
        _userBusinessLogic = new UserBusinessLogic(_mockDataAccess.Object);

        user = new User()
        {
            PhoneNumber = 1,
            Age = 1
        };
    }

    [Test]
    public void GetUserShouldReturnUser()
    {
        _mockDataAccess.Setup(g => g.CreateUser("test", 1, 1));
        //Arrange
        User user = _userBusinessLogic.GetUser("test");

        Assert.NotNull(user);
    }

But this returns null when it should return a user and therefore Not Null.

Any help will be appreciated

Upvotes: 0

Views: 655

Answers (1)

Markus
Markus

Reputation: 22501

Up to now, the code contains a setup for CreateUser, but not a setup for GetUser. As you want to test that CreateUser is called, you need to setup GetUser in a way that for the first call returns null, but the second returns the created user. You can achieve this with SetupSequence:

[Test]
public void GetUserShouldReturnUser()
{
    // Arrange
    var createdUser = new User() {};
    _mockDataAccess.Setup(g => g.CreateUser("test", 1, 1));
    _mockDataAccess.SetupSequence(g => g.GetUser("test"))
        .Returns((User)null)
        .Returns(createdUser);
    // Act
    User user = _userBusinessLogic.GetUser("test");
    // Assert
    Assert.NotNull(user);
    Assert.Same(createdUser, user);
}

As a sidenote: the code in its current form is prone to race conditions. If the method is called twice, it might attempt to create the same user twice.

Upvotes: 1

Related Questions