-
Notifications
You must be signed in to change notification settings - Fork 11
Add unit tests for UsersController API endpoints #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,188 @@ | ||||||||||
| using Microsoft.AspNetCore.Mvc; | ||||||||||
| using Microsoft.Extensions.Logging; | ||||||||||
| using Moq; | ||||||||||
| using NetUsersApi.Controllers; | ||||||||||
| using NetUsersApi.Models; | ||||||||||
|
|
||||||||||
| namespace NetUsersApi.Tests.Controllers; | ||||||||||
|
|
||||||||||
| public class UsersControllerTests | ||||||||||
| { | ||||||||||
| private readonly Mock<ILogger<UsersController>> _mockLogger; | ||||||||||
| private readonly UsersController _controller; | ||||||||||
|
|
||||||||||
| public UsersControllerTests() | ||||||||||
| { | ||||||||||
| _mockLogger = new Mock<ILogger<UsersController>>(); | ||||||||||
| _controller = new UsersController(_mockLogger.Object); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| [Fact] | ||||||||||
| public void GetUsers_ReturnsAllUsers() | ||||||||||
| { | ||||||||||
| // Arrange | ||||||||||
| // The controller uses static data, so we can just call it | ||||||||||
|
|
||||||||||
| // Act | ||||||||||
| var result = _controller.GetUsers(); | ||||||||||
|
|
||||||||||
| // Assert | ||||||||||
| var okResult = Assert.IsType<OkObjectResult>(result.Result); | ||||||||||
| var users = Assert.IsAssignableFrom<IEnumerable<UserProfile>>(okResult.Value); | ||||||||||
| Assert.NotEmpty(users); | ||||||||||
| } | ||||||||||
|
Comment on lines
+20
to
+33
|
||||||||||
|
|
||||||||||
| [Theory] | ||||||||||
| [InlineData("1")] | ||||||||||
| [InlineData("2")] | ||||||||||
| [InlineData("3")] | ||||||||||
| public void GetUser_ValidId_ReturnsUser(string id) | ||||||||||
| { | ||||||||||
| // Act | ||||||||||
| var result = _controller.GetUser(id); | ||||||||||
|
|
||||||||||
| // Assert | ||||||||||
| var okResult = Assert.IsType<OkObjectResult>(result.Result); | ||||||||||
| var user = Assert.IsType<UserProfile>(okResult.Value); | ||||||||||
| Assert.Equal(id, user.Id); | ||||||||||
| } | ||||||||||
|
Comment on lines
+35
to
+48
|
||||||||||
|
|
||||||||||
| [Fact] | ||||||||||
| public void GetUser_InvalidId_ReturnsNotFound() | ||||||||||
| { | ||||||||||
| // Arrange | ||||||||||
| var invalidId = "999"; | ||||||||||
|
|
||||||||||
| // Act | ||||||||||
| var result = _controller.GetUser(invalidId); | ||||||||||
|
|
||||||||||
| // Assert | ||||||||||
| var notFoundResult = Assert.IsType<NotFoundObjectResult>(result.Result); | ||||||||||
| Assert.NotNull(notFoundResult.Value); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| [Fact] | ||||||||||
| public void CreateUser_ValidUser_ReturnsCreatedAtAction() | ||||||||||
| { | ||||||||||
| // Arrange | ||||||||||
| var newUser = new UserProfile | ||||||||||
| { | ||||||||||
| Id = "100", | ||||||||||
| FullName = "Test User", | ||||||||||
| Emoji = "🧪" | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| // Act | ||||||||||
| var result = _controller.CreateUser(newUser); | ||||||||||
|
|
||||||||||
| // Assert | ||||||||||
| var createdResult = Assert.IsType<CreatedAtActionResult>(result.Result); | ||||||||||
| var returnedUser = Assert.IsType<UserProfile>(createdResult.Value); | ||||||||||
| Assert.Equal(newUser.Id, returnedUser.Id); | ||||||||||
| Assert.Equal(newUser.FullName, returnedUser.FullName); | ||||||||||
| Assert.Equal(newUser.Emoji, returnedUser.Emoji); | ||||||||||
| Assert.Equal(nameof(UsersController.GetUser), createdResult.ActionName); | ||||||||||
| } | ||||||||||
|
Comment on lines
+65
to
+85
|
||||||||||
|
|
||||||||||
| [Fact] | ||||||||||
| public void CreateUser_NullUser_ReturnsBadRequest() | ||||||||||
| { | ||||||||||
| // Arrange | ||||||||||
| UserProfile? nullUser = null; | ||||||||||
|
|
||||||||||
| // Act | ||||||||||
| var result = _controller.CreateUser(nullUser!); | ||||||||||
|
||||||||||
| var result = _controller.CreateUser(nullUser!); | |
| #pragma warning disable CS8625 | |
| var result = _controller.CreateUser(nullUser); | |
| #pragma warning restore CS8625 |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test isolation issue: This test mutates the shared static _users list by modifying user ID "1", which will affect all subsequent tests that depend on the original user data. This causes non-deterministic test failures when tests run in parallel or in different orders. The test should either:
- Restore the original user data after the test
- Use a setup method to reset the static list to a known state before each test
- Consider refactoring the controller to use dependency injection for the data store
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary null-forgiving operator: The null-forgiving operator (!) is used after explicitly setting nullUser to null. This suppresses a warning but doesn't add value since the test is specifically checking null handling. Consider removing the ! and addressing the compiler warning with a #pragma warning disable CS8625 if needed, or restructure the test to avoid the null assignment pattern.
| var result = _controller.UpdateUser(userId, nullUser!); | |
| var result = _controller.UpdateUser(userId, nullUser); |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test isolation issue: This test mutates the shared static _users list by modifying user ID "1", which will affect all subsequent tests that depend on the original user data. This causes non-deterministic test failures when tests run in parallel or in different orders. The test should either:
- Restore the original user data after the test
- Use a setup method to reset the static list to a known state before each test
- Consider refactoring the controller to use dependency injection for the data store
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <RootNamespace>NetUsersApi.Tests</RootNamespace> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="coverlet.collector" Version="6.0.2" /> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> | ||
| <PackageReference Include="Moq" Version="4.20.72" /> | ||
| <PackageReference Include="xunit" Version="2.9.2" /> | ||
| <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Using Include="Xunit" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\net-users-api\net-users-api.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,48 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.0.31903.59 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-users-api", "net-users-api\net-users-api.csproj", "{F2C0F837-02EA-42E5-BDC0-48C31DD4245D}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| EndGlobal | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.0.31903.59 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-users-api", "net-users-api\net-users-api.csproj", "{F2C0F837-02EA-42E5-BDC0-48C31DD4245D}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-users-api.Tests", "net-users-api.Tests\net-users-api.Tests.csproj", "{4F388C16-F623-435E-904F-00E0C80D9F87}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Debug|x64 = Debug|x64 | ||
| Debug|x86 = Debug|x86 | ||
| Release|Any CPU = Release|Any CPU | ||
| Release|x64 = Release|x64 | ||
| Release|x86 = Release|x86 | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|x64.Build.0 = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Debug|x86.Build.0 = Debug|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|x64.ActiveCfg = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|x64.Build.0 = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|x86.ActiveCfg = Release|Any CPU | ||
| {F2C0F837-02EA-42E5-BDC0-48C31DD4245D}.Release|x86.Build.0 = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|x64.Build.0 = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Debug|x86.Build.0 = Debug|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|x64.ActiveCfg = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|x64.Build.0 = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|x86.ActiveCfg = Release|Any CPU | ||
| {4F388C16-F623-435E-904F-00E0C80D9F87}.Release|x86.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| EndGlobal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test setup/teardown: The test class should implement
IDisposableor use a[Fact]setup method to reset the static_userslist inUsersControllerto its original state before each test. This would prevent test isolation issues caused by shared mutable state.Example:
Note: This would require making
_usersaccessible (currently it's private).