2012-02-20 170 views
3

我想爲我的MVC3項目(這個項目的第一個測試)編寫一些單元測試,我越來越難倒了。基本上,我有一個MemberQueries類,我的MemberController用於處理所有的邏輯。需要幫助編寫單元測試需要HttpContext.Current.User的方法

我想開始在這個類上編寫測試,並想從一個簡單的例子開始。我在這個類中有一個名爲IsEditModeAvailable的方法,該方法確定用戶是否是「站點管理員」角色的成員,或者用戶是否能夠編輯自己的數據,但沒有人會刪除。我通過將傳入的Id值與HttpContext用戶屬性進行比較來確定最後一項要求。

我遇到的問題是我不知道如何在創建MemberQueries對象時將模擬或注入適當的參數到我的單元測試中。我正在使用,NUnit,Moq和Ninject,但我不知道如何編寫代碼。如果我只是沒有正確地構建它,請告訴我,因爲我是單元測試的完全noobot。

下面是從我MemberQueries類的代碼示例:

public class MemberQueries : IMemberQueries 
{ 
    private readonly IRepository<Member> _memberRepository; 
    private readonly IMemberServices _memberServices; 
    private readonly IPrincipal _currentUser; 

    public MemberQueries(IUnitOfWork unitOfWork, IMemberServices memberServices, IPrincipal currentUser) 
    { 
    _memberRepository = unitOfWork.RepositoryFor<Member>(); 
    _memberServices = memberServices; 
    _currentUser = currentUser; 
    } 

    public bool IsEditModeAvailable(int memberIdToEdit) 
    { 
    if (_currentUser.IsInRole("Site Administrator")) return true; 
    if (MemberIsLoggedInUser(memberIdToEdit)) return true; 
    return false; 
    } 

    public bool MemberIsLoggedInUser(int memberIdToEdit) 
    { 
    var loggedInUser = _memberServices.FindByEmail(_currentUser.Identity.Name); 
    if (loggedInUser != null && loggedInUser.Id == memberIdToEdit) return true; 
    return false; 
    } 

} 

下面是從我MemberServices類樣品(這是我的域名項目,由MemberQueries引用):

public class MemberServices : IMemberServices 
{ 
    private readonly IRepository<Member> _memberRepository; 

    public MemberServices(IUnitOfWork unitOfWork) 
    { 
    _memberRepository = unitOfWork.RepositoryFor<Member>(); 
    } 

    public Member Find(int id) 
    { 
    return _memberRepository.FindById(id); 
    } 

    public Member FindByEmail(string email) 
    { 
    return _memberRepository.Find(m => m.Email == email).SingleOrDefault(); 
    } 
} 

最後,這裏是我試圖寫的單元測試的存根:

[Test] 
public void LoggedInUserCanEditTheirOwnInformation() 
{ 
    var unitOfWork = new UnitOfWork(); 

    var currentUser = new Mock<IPrincipal>(); 
    // I need to somehow tell Moq that the logged in user has a HttpContext.User.Name of "[email protected]" 

    var memberServices = new Mock<MemberServices>(); 
    // I then need to tell Moq that it's FindByEmail("[email protected]") method should return a member with a UserId of 1 

    var memberQueries = new MemberQueries(unitOfWork, memberServices.Object, currentUser.Object); 

    // If the logged in user is "[email protected]" who has an Id of 1, then IsEditModeAvailable(1) should return true 
    Assert.IsTrue(memberQueries.IsEditModeAvailable(1)); 
} 
+1

我沒有在代碼中看到任何地方引用了'HttpContext.Current'。 – 2012-02-20 21:17:49

+0

Darin,我通過ninject注入HttpContext,如下所示:'kernel.Bind ().ToMethod(ctx => HttpContext.Current.User).InRequestScope();' – bigmac 2012-02-20 21:28:02

+0

您試圖測試的代碼不取決於在上面。所以你可以很好地嘲笑一切。 – 2012-02-20 21:36:30

回答

3

它看起來像你試圖測試MemberQueries.IsEditModeAvailable方法。你有兩個案例可以覆蓋這裏。 Site Administrators大小寫以及有一個當前登錄的用戶的id與傳遞爲參數的用戶的情況。而且,由於MemberQueries類純粹依靠接口,你可以嘲笑一切:

[TestMethod] 
public void EditMode_Must_Be_Available_For_Site_Administrators() 
{ 
    // arrange 
    var unitOfWork = new Mock<IUnitOfWork>(); 
    var currentUser = new Mock<IPrincipal>(); 
    currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(true); 
    var memberServices = new Mock<IMemberServices>(); 
    var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object); 

    // act 
    var actual = memberQueries.IsEditModeAvailable(1); 

    // assert 
    Assert.IsTrue(actual); 
} 

[TestMethod] 
public void EditMode_Must_Be_Available_For_Logged_In_Users_If_His_Id_Matches() 
{ 
    // arrange 
    var unitOfWork = new Mock<IUnitOfWork>(); 
    var currentUser = new Mock<IPrincipal>(); 
    var identity = new Mock<IIdentity>(); 
    identity.Setup(x => x.Name).Returns("[email protected]"); 
    currentUser.Setup(x => x.Identity).Returns(identity.Object); 
    currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(false); 
    var memberServices = new Mock<IMemberServices>(); 
    var member = new Member 
    { 
     Id = 1 
    }; 
    memberServices.Setup(x => x.FindByEmail("[email protected]")).Returns(member); 
    var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object); 

    // act 
    var actual = memberQueries.IsEditModeAvailable(1); 

    // assert 
    Assert.IsTrue(actual); 
} 

其實有你需要覆蓋第三種情況:你有一個當前登錄的用戶,誰是不是網站管理員,其ID沒有按」 t匹配作爲參數傳遞的一個:

[TestMethod] 
public void EditMode_Should_Not_Be_Available_For_Logged_In_Users_If_His_Id_Doesnt_Match() 
{ 
    // arrange 
    var unitOfWork = new Mock<IUnitOfWork>(); 
    var currentUser = new Mock<IPrincipal>(); 
    var identity = new Mock<IIdentity>(); 
    identity.Setup(x => x.Name).Returns("[email protected]"); 
    currentUser.Setup(x => x.Identity).Returns(identity.Object); 
    currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(false); 
    var memberServices = new Mock<IMemberServices>(); 
    var member = new Member 
    { 
     Id = 2 
    }; 
    memberServices.Setup(x => x.FindByEmail("[email protected]")).Returns(member); 
    var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object); 

    // act 
    var actual = memberQueries.IsEditModeAvailable(1); 

    // assert 
    Assert.IsFalse(actual); 
} 
+1

達林,你搖滾!謝謝。這完美的作品,這將有助於我寫這個課程的其他測試。對此,我真的非常感激。 – bigmac 2012-02-20 21:44:01

2

好新s是你將用戶作爲IPrincipal傳遞給需要它的代碼而不是引用HttpContext.Current.User。所有你需要做的就是設置模擬IPrincipal,以便它返回你測試所需的值。

var mockIdentity = new Mock<IIdentity>(); 
mockIdentity.Setup(x => x.Name).Returns("[email protected]"); 

var mockPrincipal = new Mock<IPrincipal>(); 
mockPrincipal.Setup(x => x.Identity).Returns(mockIdentity.Object); 
+0

Trevor,謝謝你的迴應。它幫助我開始,至少在Darin寫出我需要的一切之前。 ;-) 我很感激。 – bigmac 2012-02-20 21:45:05