Birim testinde HttpContext.Current.Session ayarı


185

Birim testi yapmaya çalıştığım bir web servisim var. Hizmette HttpContextbenzerlerinden birkaç değer çeker :

 m_password = (string)HttpContext.Current.Session["CustomerId"];
 m_userID = (string)HttpContext.Current.Session["CustomerUrl"];

birim testte ben basit bir işçi isteği kullanarak bağlam oluşturmak, böyle:

SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter());
HttpContext context = new HttpContext(request);
HttpContext.Current = context;

Ancak, ne zaman HttpContext.Current.Session

HttpContext.Current.Session["CustomerId"] = "customer1";
HttpContext.Current.Session["CustomerUrl"] = "customer1Url";

Boş olduğunu söyleyen null başvuru özel durumu alıyorum HttpContext.Current.Session.

Ünite testinde geçerli oturumu başlatmanın bir yolu var mı?


Bu yöntemi denedin mi?
Raj Ranjhan

Mümkünse HttpContextBase kullanın .
Mart'ta jrummell

Yanıtlar:


105

Biz alay etmek zorunda kaldı HttpContextbir kullanarak HttpContextManagerve arama fabrika bizim uygulama içinden hem de birim testleri

public class HttpContextManager 
{
    private static HttpContextBase m_context;
    public static HttpContextBase Current
    {
        get
        {
            if (m_context != null)
                return m_context;

            if (HttpContext.Current == null)
                throw new InvalidOperationException("HttpContext not available");

            return new HttpContextWrapper(HttpContext.Current);
        }
    }

    public static void SetCurrentContext(HttpContextBase context)
    {
        m_context = context;
    }
}

Daha sonra herhangi bir çağrı yerini alacak HttpContext.Currentolan HttpContextManager.Currentve aynı yöntemlere erişim hakkına sahiptir. Daha sonra test ederken HttpContextManager, beklentilerinize erişebilir ve alay edebilirsiniz.

Bu, Adedi kullanan bir örnektir :

private HttpContextBase GetMockedHttpContext()
{
    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    var response = new Mock<HttpResponseBase>();
    var session = new Mock<HttpSessionStateBase>();
    var server = new Mock<HttpServerUtilityBase>();
    var user = new Mock<IPrincipal>();
    var identity = new Mock<IIdentity>();
    var urlHelper = new Mock<UrlHelper>();

    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    var requestContext = new Mock<RequestContext>();
    requestContext.Setup(x => x.HttpContext).Returns(context.Object);
    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session.Object);
    context.Setup(ctx => ctx.Server).Returns(server.Object);
    context.Setup(ctx => ctx.User).Returns(user.Object);
    user.Setup(ctx => ctx.Identity).Returns(identity.Object);
    identity.Setup(id => id.IsAuthenticated).Returns(true);
    identity.Setup(id => id.Name).Returns("test");
    request.Setup(req => req.Url).Returns(new Uri("http://www.google.com"));
    request.Setup(req => req.RequestContext).Returns(requestContext.Object);
    requestContext.Setup(x => x.RouteData).Returns(new RouteData());
    request.SetupGet(req => req.Headers).Returns(new NameValueCollection());

    return context.Object;
}

ve bunu birim testlerinizde kullanmak için bunu Test Init yöntemimde çağırıyorum

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

Daha sonra, yukarıdaki yöntemde, Oturumdan beklenen ve web hizmetinize sunulmasını beklediğiniz sonuçları ekleyebilirsiniz.


1
ancak bu SimpleWorkerRequest kullanmıyor
knocte

SimpleWorkerRequest'in HttpContext'deki değerlere erişebilmesi için HttpContext'i taklit etmeye çalışıyordu, HttpContextFactory hizmetinde kullanacaktı
Anthony Shaw

M_context destek alanının yalnızca sahte bir bağlam için (SetCurrentContext aracılığıyla ayarlandığında) döndürülmesi ve gerçek HttpContext için Current'a yapılan her çağrı için bir sarmalayıcı oluşturulması kasıtlı mıdır?
Stephen Price

Evet öyle. m_context HttpContextBase türündedir ve dönen HttpContextWrapper, HttpContextBase öğesini Geçerli HttpContext ile döndürür
Anthony Shaw

1
HttpContextManagerdaha iyi bir isim olurdu HttpContextSourceama HttpContextFactoryyanıltıcı olduğunu kabul ediyorum .
de programlama profesörü

298

Bunun HttpContextgibi yeni bir şey oluşturarak "taklit edebilirsiniz" :

http://www.necronet.org/archive/2010/07/28/unit-testing-code-that-uses-httpcontext-current-session.aspx

Bu kodu aldım ve böyle statik bir yardımcı sınıf üzerine koydum:

public static HttpContext FakeHttpContext()
{
    var httpRequest = new HttpRequest("", "http://example.com/", "");
    var stringWriter = new StringWriter();
    var httpResponse = new HttpResponse(stringWriter);
    var httpContext = new HttpContext(httpRequest, httpResponse);

    var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
                                            new HttpStaticObjectsCollection(), 10, true,
                                            HttpCookieMode.AutoDetect,
                                            SessionStateMode.InProc, false);

    httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
                                BindingFlags.NonPublic | BindingFlags.Instance,
                                null, CallingConventions.Standard,
                                new[] { typeof(HttpSessionStateContainer) },
                                null)
                        .Invoke(new object[] { sessionContainer });

    return httpContext;
}

Veya bunun yerine yeni inşa etmek yansıma kullanmanın HttpSessionStateörneğini, sadece ekleyebilirsiniz HttpSessionStateContainerüzere HttpContext(Brent M. büyü açıklama uyarınca):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

ve sonra ünite testlerinizde şöyle arayabilirsiniz:

HttpContext.Current = MockHelper.FakeHttpContext();

24
Bu yanıtı kabul edilen yanıttan daha çok seviyorum çünkü test faaliyetlerinizi desteklemek için üretim kodunuzu değiştirmek kötü bir uygulamadır. Verilmiş, üretim kodunuz böyle üçüncü taraf ad alanlarını soyutlamalıdır, ancak eski kodla çalışırken her zaman bu kontrol veya yeniden faktör lüksüne sahip olmazsınız.
Sean Glover

29
Yeni HttpSessionState örneğini oluşturmak için yansıma kullanmanız gerekmez. HttpSessionStateContainer'ınızı HttpContext'e SessionStateUtility.AddHttpSessionStateToContext'i kullanarak ekleyebilirsiniz.
Brent M. Spell

MockHelper sadece statik yöntemin olduğu sınıfın adıdır, tercih ettiğiniz adı kullanabilirsiniz.
Milox

Cevabınızı uygulamaya çalıştım ama Oturum hala boş. Lütfen Post adresime bir göz atar mısınız stackoverflow.com/questions/23586765/… . Teşekkür ederim
Joe

Server.MapPath()bunu da kullanırsanız çalışmaz.
Yuck

45

Milox çözümü kabul edilen bir IMHO'dan daha iyidir, ancak sorgu dizgisi ile URL'leri işlerken bu uygulamada bazı sorunlar yaşadım .

Herhangi bir url ile düzgün çalışması ve Yansımadan kaçınmak için bazı değişiklikler yaptım.

public static HttpContext FakeHttpContext(string url)
{
    var uri = new Uri(url);
    var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
                                        uri.Query.TrimStart('?'));
    var stringWriter = new StringWriter();
    var httpResponse = new HttpResponse(stringWriter);
    var httpContext = new HttpContext(httpRequest, httpResponse);

    var sessionContainer = new HttpSessionStateContainer("id",
                                    new SessionStateItemCollection(),
                                    new HttpStaticObjectsCollection(),
                                    10, true, HttpCookieMode.AutoDetect,
                                    SessionStateMode.InProc, false);

    SessionStateUtility.AddHttpSessionStateToContext(
                                         httpContext, sessionContainer);

    return httpContext;
}

Bu sahte için izin verir httpContext.Session, nasıl aynı şeyi yapmak için herhangi bir fikir httpContext.Application?
KyleMit

39

Bir süre önce bununla ilgili bir şeyi kötüleştiriyorum.

MVC3 .NET HttpContext.Current.Session Birim Testi

Umarım yardımcı olur.

[TestInitialize]
public void TestSetup()
{
    // We need to setup the Current HTTP Context as follows:            

    // Step 1: Setup the HTTP Request
    var httpRequest = new HttpRequest("", "http://localhost/", "");

    // Step 2: Setup the HTTP Response
    var httpResponce = new HttpResponse(new StringWriter());

    // Step 3: Setup the Http Context
    var httpContext = new HttpContext(httpRequest, httpResponce);
    var sessionContainer = 
        new HttpSessionStateContainer("id", 
                                       new SessionStateItemCollection(),
                                       new HttpStaticObjectsCollection(), 
                                       10, 
                                       true,
                                       HttpCookieMode.AutoDetect,
                                       SessionStateMode.InProc, 
                                       false);
    httpContext.Items["AspSession"] = 
        typeof(HttpSessionState)
        .GetConstructor(
                            BindingFlags.NonPublic | BindingFlags.Instance,
                            null, 
                            CallingConventions.Standard,
                            new[] { typeof(HttpSessionStateContainer) },
                            null)
        .Invoke(new object[] { sessionContainer });

    // Step 4: Assign the Context
    HttpContext.Current = httpContext;
}

[TestMethod]
public void BasicTest_Push_Item_Into_Session()
{
    // Arrange
    var itemValue = "RandomItemValue";
    var itemKey = "RandomItemKey";

    // Act
    HttpContext.Current.Session.Add(itemKey, itemValue);

    // Assert
    Assert.AreEqual(HttpContext.Current.Session[itemKey], itemValue);
}

Çok iyi ve basit çalışıyor ... Teşekkürler!
mggSoft

12

MVC çerçevesini kullanıyorsanız, bunun çalışması gerekir. Ben kullanılan taşınma olayı en FakeHttpContext'i ve birkaç kod daha ekledim. Fikir şu yayından geldi:

http://codepaste.net/p269t8

Bu MVC 5'te çalışıyor gibi görünüyor. Ben MVC önceki sürümlerinde bunu denemedim.

HttpContext.Current = MockHttpContext.FakeHttpContext();

var wrapper = new HttpContextWrapper(HttpContext.Current);

MyController controller = new MyController();
controller.ControllerContext = new ControllerContext(wrapper, new RouteData(), controller);

string result = controller.MyMethod();

3
Bağlantı koptu, belki kodu bir dahaki sefere buraya koyabilirsiniz.
Rhyous

11

FakeHttpContext'i deneyebilirsiniz :

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}

Harika ve kullanımı çok kolay
Beanwah

8

Asp.net Core / MVC 6 rc2'de HttpContext

var SomeController controller = new SomeController();

controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();

rc 1 (önceki değeri)

var SomeController controller = new SomeController();

controller.ActionContext = new ActionContext();
controller.ActionContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();

https://stackoverflow.com/a/34022964/516748

Kullanmayı düşünün Moq

new Mock<ISession>();

7

Benimle çalışan cevap @ Anthony'nin yazdığı şeydi, ama başka bir satır eklemelisin.

    request.SetupGet(req => req.Headers).Returns(new NameValueCollection());

böylece bunu kullanabilirsiniz:

HttpContextFactory.Current.Request.Headers.Add(key, value);

2

Bunu dene:

        // MockHttpSession Setup
        var session = new MockHttpSession();

        // MockHttpRequest Setup - mock AJAX request
        var httpRequest = new Mock<HttpRequestBase>();

        // Setup this part of the HTTP request for AJAX calls
        httpRequest.Setup(req => req["X-Requested-With"]).Returns("XMLHttpRequest");

        // MockHttpContextBase Setup - mock request, cache, and session
        var httpContext = new Mock<HttpContextBase>();
        httpContext.Setup(ctx => ctx.Request).Returns(httpRequest.Object);
        httpContext.Setup(ctx => ctx.Cache).Returns(HttpRuntime.Cache);
        httpContext.Setup(ctx => ctx.Session).Returns(session);

        // MockHttpContext for cache
        var contextRequest = new HttpRequest("", "http://localhost/", "");
        var contextResponse = new HttpResponse(new StringWriter());
        HttpContext.Current = new HttpContext(contextRequest, contextResponse);

        // MockControllerContext Setup
        var context = new Mock<ControllerContext>();
        context.Setup(ctx => ctx.HttpContext).Returns(httpContext.Object);

        //TODO: Create new controller here
        //      Set controller's ControllerContext to context.Object

Ve sınıfı ekleyin:

public class MockHttpSession : HttpSessionStateBase
{
    Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>();
    public override object this[string name]
    {
        get
        {
            return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null;
        }
        set
        {
            _sessionDictionary[name] = value;
        }
    }

    public override void Abandon()
    {
        var keys = new List<string>();

        foreach (var kvp in _sessionDictionary)
        {
            keys.Add(kvp.Key);
        }

        foreach (var key in keys)
        {
            _sessionDictionary.Remove(key);
        }
    }

    public override void Clear()
    {
        var keys = new List<string>();

        foreach (var kvp in _sessionDictionary)
        {
            keys.Add(kvp.Key);
        }

        foreach(var key in keys)
        {
            _sessionDictionary.Remove(key);
        }
    }
}

Bu hem oturum hem de önbellek ile test etmenizi sağlar.


1

Yukarıda belirtilen seçeneklerden biraz daha az invaziv bir şey arıyordum. Sonunda sevimsiz bir çözüm buldum, ancak bazı insanlar biraz daha hızlı hareket edebilir.

Önce bir TestSession sınıfı oluşturdum :

class TestSession : ISession
{

    public TestSession()
    {
        Values = new Dictionary<string, byte[]>();
    }

    public string Id
    {
        get
        {
            return "session_id";
        }
    }

    public bool IsAvailable
    {
        get
        {
            return true;
        }
    }

    public IEnumerable<string> Keys
    {
        get { return Values.Keys; }
    }

    public Dictionary<string, byte[]> Values { get; set; }

    public void Clear()
    {
        Values.Clear();
    }

    public Task CommitAsync()
    {
        throw new NotImplementedException();
    }

    public Task LoadAsync()
    {
        throw new NotImplementedException();
    }

    public void Remove(string key)
    {
        Values.Remove(key);
    }

    public void Set(string key, byte[] value)
    {
        if (Values.ContainsKey(key))
        {
            Remove(key);
        }
        Values.Add(key, value);
    }

    public bool TryGetValue(string key, out byte[] value)
    {
        if (Values.ContainsKey(key))
        {
            value = Values[key];
            return true;
        }
        value = new byte[0];
        return false;
    }
}

Sonra denetleyicimin yapıcısına isteğe bağlı bir parametre ekledim. Parametre varsa, oturum manipülasyonu için kullanın. Aksi takdirde, HttpContext.Session öğesini kullanın:

class MyController
{

    private readonly ISession _session;

    public MyController(ISession session = null)
    {
        _session = session;
    }


    public IActionResult Action1()
    {
        Session().SetString("Key", "Value");
        View();
    }

    public IActionResult Action2()
    {
        ViewBag.Key = Session().GetString("Key");
        View();
    }

    private ISession Session()
    {
        return _session ?? HttpContext.Session;
    }
}

Şimdi TestSession'ımı denetleyiciye enjekte edebilirim :

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}

Çözümünü gerçekten seviyorum.
ÖPÜ

1

Asla alay etmeyin .. asla! Çözüm oldukça basit. Neden böyle güzel bir yaratım sahte HttpContext?

Oturumu aşağı itin! (Sadece bu çizgi çoğumuzun anlaması için yeterlidir, ancak aşağıda ayrıntılı olarak açıklanmıştır)

(string)HttpContext.Current.Session["CustomerId"];şimdi ona nasıl eriştiğimiz. Bunu şu şekilde değiştir:

_customObject.SessionProperty("CustomerId")

Testten çağrıldığında, _customObject alternatif mağaza kullanır (DB veya bulut anahtar değeri [ http://www.kvstore.io/] )

Ancak gerçek uygulamadan çağrıldığında _customObjectkullanır Session.

bu nasıl yapılır? iyi ... Bağımlılık Enjeksiyonu!

Böylece test oturumu ayarlayabilir (yeraltında) ve sonra uygulama yöntemini oturum hakkında hiçbir şey bilmiyormuş gibi çağırabilir. Ardından, uygulama kodunun oturumu doğru şekilde güncelleyip güncellemediğini gizlice kontrol edin. Veya uygulama, test tarafından ayarlanan oturum değerine göre davranırsa.

Aslında, "asla alay etmeyin" dememe rağmen alaycı olduk. Yardım edemediğimiz için bir sonraki kurala geçelim, "en az acı veren yere alay et!". Büyük HttpContextalay etmek ya da en az acı veren küçük bir oturum alay etmek? bana bu kuralların nereden geldiğini sorma. Sadece sağduyu diyelim. Ünite testi bizi öldürdüğü için alay etmeme hakkında ilginç bir okuma


0

Cevap @Ro Hit verdiği bana çok yardımcı oldu, ancak kimlik doğrulama birimi testi için bir kullanıcıyı sahte yapmak zorunda kaldım çünkü kullanıcı kimlik bilgilerini . Bu yüzden, nasıl çözdüğümü açıklayayım.

Göre bu , sen yöntemi eklerseniz

    // using System.Security.Principal;
    GenericPrincipal FakeUser(string userName)
    {
        var fakeIdentity = new GenericIdentity(userName);
        var principal = new GenericPrincipal(fakeIdentity, null);
        return principal;
    }

ve sonra ekle

    HttpContext.Current.User = FakeUser("myDomain\\myUser");

TestSetupyaptığınız yöntemin son satırına kullanıcı kimlik bilgileri eklenir ve kimlik doğrulama testi için kullanılmaya hazırdır.

Ayrıca .MapPath()yöntem gibi HttpContext ihtiyacınız olabilecek diğer bölümleri olduğunu fark ettim . Burada açıklanan ve NuGet üzerinden kurulabilen bir FakeHttpContext var .



0

Bu şekilde dene ..

public static HttpContext getCurrentSession()
  {
        HttpContext.Current = new HttpContext(new HttpRequest("", ConfigurationManager.AppSettings["UnitTestSessionURL"], ""), new HttpResponse(new System.IO.StringWriter()));
        System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
        HttpContext.Current, new HttpSessionStateContainer("", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 20000, true,
        HttpCookieMode.UseCookies, SessionStateMode.InProc, false));
        return HttpContext.Current;
  }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.