Reputation: 15
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
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