使用 NUnit 和 Moq 测试控制器时的 NullReference

NullReference durning testing controller with NUnit and Moq

我在测试中遇到 NullReference 异常。当我在控制器中评论 eventsRepository.AddEvent(eve, User.Identity.GetUserId()); 时。

我该如何解决?

控制器方法

[ValidateAntiForgeryToken]
[HttpPost]
[Authorize]
public ActionResult CreateEvent(Event eve)
{
    if (eve.DateOfBegining < DateTime.Now)
    {
        ModelState.AddModelError("DateOfBegining", "");
    }

    if (eve.MaxQuantityOfPlayers < eve.MinCount)
    {
        ModelState.AddModelError("MinCount", "");
    }

    if (eve.ConflictSides.Count < 2 || eve.ConflictSides.Count > 10)
    {
        ModelState.AddModelError("ConflictSides", "");
    }

    if (!ModelState.IsValid)
    {
        return View("CreateEvent", eve);
    }
    else
    {
        eventsRepository.AddEvent(eve, User.Identity.GetUserId());
        return RedirectToAction("EventsList");
    }
}

添加事件

void AddEvent(Event ev, string userId);

测试方法

[TestMethod]
public void CreateEvent_AddEvent_returns_EventsList()
{
    // arrange
    var EventRepo = new Mock<IEventRepository>();
    var ParticipantsRepo = new Mock<IParticipants>();

    DateTime dt = new DateTime(2200, 1, 23);
    Event eve = new Event() 
    {
        ConflictSides = new List<ConflictSide>() { 
                                                new ConflictSide{ Name ="niebiescy"},
                                                new ConflictSide{ Name ="czerwoni"},
                                                new ConflictSide{ Name ="fioletowi"},
                                                },
        DateOfBegining = dt,
        Description = "bardzo dlugi opid na potrzeby testu",
        EventCreator= "userId",
        EventName = "najlepsza",
        FpsLimitInBuildings=300,
        FpsLimitOnOpenField=500,
        Hicap = new MagazineTyp(){ifAllow = true, ifOnlySemi = false},
        MidCap = new MagazineTyp(){ifAllow = true, ifOnlySemi = false},
        LowCap = new MagazineTyp(){ifAllow = true, ifOnlySemi = false},
        RealCap = new MagazineTyp(){ifAllow = true, ifOnlySemi = false},
        MaxQuantityOfPlayers = 50,
        MinCount = 10           
    };

    var target = new EventController(EventRepo.Object, ParticipantsRepo.Object);

    // act

    RedirectToRouteResult result = target.CreateEvent(eve) as RedirectToRouteResult;

    // assert

    // EventRepo.Verify(a => a.AddEvent(It.IsAny<Event>(), It.IsAny<string>()), Times.Once());

    Assert.AreEqual("EventsList", result.RouteValues["action"]);
}

您正在访问 User.Identity.GetUserId() 但控制器的 User 属性 未在您的测试方法中设置,因此在访问时它将为空

您需要使用虚拟用户帐户设置控制器上下文。这是一个帮助程序 class,您可以使用它来模拟获取用户主体所需的 HttpContext

private class MockHttpContext : HttpContextBase {
    private readonly IPrincipal user;

    public MockHttpContext(string username, string[] roles = null) {
        var identity = new GenericIdentity(username);
        var principal = new GenericPrincipal(identity, roles ?? new string[] { });
        user = principal;
    }

    public override IPrincipal User {
        get {
            return user;
        }
        set {
            base.User = value;
        }
    }
}

在初始化目标控制器后的测试中,您需要设置控制器上下文

//...other coder

var target = new EventController(EventRepo.Object, ParticipantsRepo.Object);
target.ControllerContext = new ControllerContext {
    Controller = target,
    HttpContext = new MockHttpContext("fakeuser@example.com")
};

//...other coder