Moq单元测试空引用异常解决及徽章方法测试咨询
First, let's break down what we need to accomplish: verify that your Badge method creates and adds a new PlayerBadge when one doesn't exist for the given pID and bID. The "Object reference not set to an instance of an object" error almost always stems from improperly mocking your EliminationDbContext or its PlayerBadges DbSet.
Step 1: Mock the DbContext and DbSet
To make your query logic work with Moq, you need to set up a mock DbSet that supports queryable operations (like Where and Any). Here's a reusable helper method to create a mock DbSet from a list:
private static Mock<DbSet<T>> CreateMockDbSet<T>(List<T> data) where T : class { var queryableData = data.AsQueryable(); var mockSet = new Mock<DbSet<T>>(); // Set up queryable behavior mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryableData.Provider); mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryableData.Expression); mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryableData.ElementType); mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator()); // Track additions to the list mockSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>(data.Add); return mockSet; }
Step 2: Write the Unit Test for Adding a New Badge
Let's build a test that verifies the method adds a new PlayerBadge when none exists. We'll cover setup, execution, and assertions:
[Test] public void Badge_WhenPlayerBadgeDoesNotExist_AddsNewEntry() { // Arrange var testPlayerId = 1; var testBadgeId = 2; var testGameId = 3; // Start with an empty list of PlayerBadges var playerBadges = new List<PlayerBadge>(); var mockPlayerBadgesSet = CreateMockDbSet(playerBadges); // Mock the DbContext to return our mock DbSet var mockContext = new Mock<EliminationDbContext>(); mockContext.Setup(c => c.PlayerBadges).Returns(mockPlayerBadgesSet.Object); // Instantiate your service/class (replace with your actual class name) var badgeManager = new BadgeManager(mockContext.Object); // Act var result = badgeManager.Badge(testPlayerId, testBadgeId, testGameId); // Assert // Check the method returned success (assuming true means success) Assert.IsTrue(result); // Verify one new badge was added Assert.AreEqual(1, playerBadges.Count); // Confirm the new badge has the correct properties var addedBadge = playerBadges.First(); Assert.AreEqual(testPlayerId, addedBadge.PlayerID); // Adjust to match your model's property name Assert.AreEqual(testBadgeId, addedBadge.BadgeID); Assert.AreEqual(testGameId, addedBadge.GameID); // Ensure the Add method was called exactly once mockPlayerBadgesSet.Verify(m => m.Add(It.IsAny<PlayerBadge>()), Times.Once); }
Fixing the Null Reference Error
This error typically occurs for one of these reasons:
- Unmocked DbSet: If you forget to set up
mockContext.Setup(c => c.PlayerBadges).Returns(...),PlayerBadgeswill be null when your method tries to access it. - Null navigation properties: If your query uses
x.Player.PlayerIDorx.Badge.BadgeIDon existing entries, you need to initialize those navigation properties in your mock data. For example:var existingBadge = new PlayerBadge { Player = new Player { PlayerID = testPlayerId }, Badge = new Badge { BadgeID = testBadgeId } }; - Hardcoded DbContext instance: If your class creates a new
EliminationDbContextinstead of using dependency injection, your mock won't be used—leading to null references or real database calls.
Testing the Case Where the Badge Already Exists
Don't forget to test the opposite scenario: when the badge exists, the method shouldn't add a new one:
[Test] public void Badge_WhenPlayerBadgeExists_DoesNotAddNewEntry() { // Arrange var testPlayerId = 1; var testBadgeId = 2; var existingBadge = new PlayerBadge { PlayerID = testPlayerId, BadgeID = testBadgeId }; var playerBadges = new List<PlayerBadge> { existingBadge }; var mockPlayerBadgesSet = CreateMockDbSet(playerBadges); var mockContext = new Mock<EliminationDbContext>(); mockContext.Setup(c => c.PlayerBadges).Returns(mockPlayerBadgesSet.Object); var badgeManager = new BadgeManager(mockContext.Object); // Act var result = badgeManager.Badge(testPlayerId, testBadgeId); // Assert // Assuming false means the badge already exists Assert.IsFalse(result); // Verify no new badges were added Assert.AreEqual(1, playerBadges.Count); // Ensure Add was never called mockPlayerBadgesSet.Verify(m => m.Add(It.IsAny<PlayerBadge>()), Times.Never); }
Key Takeaways
- Always mock DbSets with queryable behavior to support LINQ operations like
WhereandAny. - Use dependency injection to pass the mock context to your class—avoid hardcoding context instances.
- Initialize navigation properties in mock data if your query relies on them.
- Verify both the method's return value and side effects (like
Addbeing called) to ensure full coverage.
内容的提问来源于stack exchange,提问作者hmad

