I do not have much experience with testing in .NET. I ran into an issue that I do not understand how to solve. Please advise where I can read about it.
I have a backend code that works with a simple database. I already created a frontend for it and everything works fine. But I wanted to add unit testing. I created a test project (VS template, xUnit) and added dependency Microsoft.EntityFrameworkCore.InMemory.
All my tests work, except a test for PUT. I receive the error: System.InvalidOperationException : The instance of entity type 'UserTask' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. I tried to use .sqlite in memory instead of .inmemory and got the same error. My understanding is that I am supposed to test how _context.Entry(task).State = EntityState.Modified; await _context.SaveChangesAsync(); works and for some reason, I cannot test it because it doesn't detach the entity that I am replacing. ChatGPT suggested detaching it manually but then what am I testing there - it doesn't look to me like a valid test. Also, I found that I can use Context.Entry(entity).Reload(), but because I have a problem inside of the line var postResult = await _controller.PutTask(1,newTask), I don't think I can apply it.
Please direct me where I can find information about how to test this method.
In my controller in the main project:
// PUT: api/Task/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTask(int id, [FromBody] UserTask task)
{
if (id != task.Id ||
!Enum.IsDefined(typeof(StatusEnum), task.Status) ||
!Enum.IsDefined(typeof(PriorityEnum), task.Priority))
{ return BadRequest(); }
_context.Entry(task).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TaskExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
In my test file in the test project:
public class TaskControllerTests : IDisposable
{
private readonly UserTasksContext _context;
private readonly TaskController _controller;
public TaskControllerTests()
{
var options = new DbContextOptionsBuilder<UserTasksContext>()
.UseInMemoryDatabase("TaskManagerTestDb")
.Options;
_context = new UserTasksContext(options);
_controller = new TaskController(_context);
SeedDatabase();
}
private void SeedDatabase()
{
_context.UserTasks.AddRange(
new UserTask { Id = 1, Title = "Task 1", DueDate = DateTime.Now, Priority = "High", Status = "Pending" },
new UserTask { Id = 2, Title = "Task 2", DueDate = DateTime.Now, Priority = "Low", Status = "Completed" }
);
_context.SaveChanges();
}
public void Dispose()
{
_context.Database.EnsureDeleted();
_context.Dispose();
}
[Fact]
public async Task PutTask_EditsNew_ValidTask()
{
// Arrange
var newTask = new UserTask {Id=1, Title = "New Task", DueDate = DateTime.Now.AddDays(3), Priority = "VeryHigh", Status = "InProgress" };
// Act
var postResult = await _controller.PutTask(1,newTask);
var getResult = await _controller.GetTasks();
// Assert
var actionResult = Assert.IsType<ActionResult<IEnumerable<UserTask>>>(getResult);
var tasks = Assert.IsAssignableFrom<IEnumerable<UserTask>>(actionResult.Value);
Assert.Equal(2, tasks.Count());
Assert.NotNull(tasks?.LastOrDefault());
Assert.Equal("New Task", tasks?.FirstOrDefault()?.Title);
Assert.Equal("InProgress", tasks?.FirstOrDefault()?.Status);
}
}