Click here to Skip to main content
14,036,655 members
Rate this:
 
Please Sign up or sign in to vote.
I'm writing APIs using c#.Net Core

One of my APIs is used to generate captcha then store to the session, I'm using Redis to store session here.

And now I'm trying to write a unit test for the APIs.
Cause the login API needed to pass captcha, so I need to get the captcha value from the session.
I'm using Moq to write the unit test.
My code is below:

Captcha api:
ILogger<CommonApiController> _logger;
private readonly ISessionWapper _sessionWapper;
string _captcha;

public CommonApiController(ISessionWapper sessionWapper, BaseContext db, IOptions<JwtSetting> options, ILogger<CommonApiController> logger,
            IOptions<SettingModel> sysSetting)
            : base(db, options, logger, sysSetting)
        {
            _logger = logger;
            _sessionWapper = sessionWapper;
        }
public CommonApiController(ISessionWapper sessionWapper, ILogger<CommonApiController> logger)
          : base()
        {
            _logger = logger;
            _sessionWapper = sessionWapper;
        }

[HttpGet("Captcha")]
[ProducesResponseType(typeof(FileResult), 200)]
public async Task<FileResult> GetCaptcha()
{
            try
            {
                var captcha = Captcha.GenerateRandomText(4);
             
                await _sessionWapper.Set(captcha);
                var session = await _sessionWapper.Get();
                _captcha = session;
                _logger.LogDebug(session);
                return File(Captcha.GenerateCaptchaImage(captcha), "image/gif");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.StackTrace);
                return File("ERROR", "image/gif");
            }
        }


Session helper:
public static class SessionExtensions
{
    public static void SetObject<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static T GetObject<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
    }
}
public interface ISessionWapper
{
    CaptchaSessionModel Captcha { get; set; }
    Task<string> Get();
    Task Set(string value);
}
public class SessionWapper : ISessionWapper
{
    private static readonly string _captchaKey = "captchaKey";
    private readonly IHttpContextAccessor _httpContextAccessor;

    public SessionWapper(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    public void Clear()
    {
        Session.Clear();
    }
    private ISession Session
    {
        get => _httpContextAccessor.HttpContext.Session;
    }
    public async Task<string> Get()
    {
        await _httpContextAccessor.HttpContext.Session.LoadAsync();
        return Captcha.value;
    }
    public async Task Set(string value)
    {
        Captcha = new CaptchaSessionModel { value = value };
        await _httpContextAccessor.HttpContext.Session.CommitAsync();
    }
    public CaptchaSessionModel Captcha
    {
        get => Session.GetObject<CaptchaSessionModel>(_captchaKey);
        set => Session.SetObject(_captchaKey, value);
    }

}
public struct CaptchaSessionModel
{
    public string value { get; set; }
}


Login Api:
private readonly ISessionWapper _sessionWapper;

ILogger<LoginController> _logger;

public LoginController(ISessionWapper sessionWapper, BaseContext db, IOptions<JwtSetting> options, ILogger<LoginController> logger, IOptions<SettingModel> settingModel)
            : base(db, options, logger, settingModel)
        {
            _logger = logger;
            _sessionWapper = sessionWapper;
        }

[HttpPost("Login/Login")]
        [ProducesResponseType(typeof(DisplayResponseLogin), 200)]
        public async Task<IActionResult> Login([FromBody]LoginRequestModel model)
        {
            try
            {
                var session = await _sessionWapper.Get();
                _logger.LogDebug(JsonConvert.SerializeObject(_sessionWapper.Captcha));

                _logger.LogDebug(session);
                if (string.IsNullOrEmpty(session) || !model.captcha.Equals(session))
                {
                    return Result(new BaseDalResponseModel { status = MobileHis.Enums.ResponseStatus.CaptchaIncorrect_3 });
                }
                
//some login code ....

                var now = DateTime.UtcNow;

                JwtHelper jwtHelper = new JwtHelper(dal, _options);
                var encodedJwt = jwtHelper.GenerateToken(acc, model.remember);//, apis);

                var response = new LoginResponseModel
                {
                    access_token = encodedJwt,
                    expires_in = (int)_options.Expire,
                   
                };
               

                return Result(new BaseDalResponseModel { status = MobileHis.Enums.ResponseStatus.OK_0, content = response });

            }
            catch (Exception ex)
            {
                Logging(_logger, LoggingEvents.Login, ex.Message, MobileHis.Enums.ResponseStatus.Error_99);
                return BadRequest();
            }

        }


Startup.cs
public void ConfigureServices(IServiceCollection services)
        {
          
            services.AddMvc();

            //services.AddDistributedMemoryCache(); 
            services.AddDistributedRedisCache(options =>
            {
                options.Configuration = "localhost:6379";
            });
            services.AddSession(options =>
            {
              
                options.Cookie.Name = "cookieName";
                options.IdleTimeout = TimeSpan.FromMinutes(5);
            });

          
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<ISessionWapper, SessionWapper>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
           
            // This session call MUST go before UseMvc()
            app.UseSession();

            //base
            app.UseMvc();
        }


What I have tried:

Here is my unit test code
[Fact]
       public async Task TestGetCaptcha()
       {
           ILoggerFactory f = new LoggerFactory();
           ILogger<CommonApiController> logger = f.CreateLogger<CommonApiController>();

           // Arrange
           var mockRepo = new Mock<ISessionWapper>();
           var controller = new CommonApiController(mockRepo.Object, logger);

           // Act
           var result = await controller.GetCaptcha();
           var captcha = //how to get captcha value here?
           // Assert
           var redirectToActionResult =
               Assert.IsType<RedirectToActionResult>(result);
           Assert.Equal("Home", redirectToActionResult.ControllerName);
           Assert.Equal("Index", redirectToActionResult.ActionName);
       }


or I should mock the server?
public class TestClientProvider : IDisposable
   {
       private TestServer _server;
       public HttpClient Client { get; private set; }

       public TestClientProvider(string baseAddress= "http://myapp.localhost:5000")
       {
          var server = new Mock<TestServer>(new WebHostBuilder().UseStartup<Startup>());
           _server = server.Object;
           //_server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
           Client = _server.CreateClient();
           Client.BaseAddress = new Uri(baseAddress);
           Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); ;
       }

       public void Dispose()
       {
           _server?.Dispose();
           Client?.Dispose();
       }
   }



Thank you!
Posted
Updated 19-Dec-18 21:19pm
Comments
F-ES Sitecore 20-Dec-18 6:52am
   
unit tests shouldn't be accessing any external resources, it shouldn't call urls, it shouldn't depend on the HttpApplication etc. What you need to do is use moq to set up methods on your mocked ISessionWrapper so that they return hard-coded values if the Get\Set methods are called with the expected parameters.
Iris Shing 20-Dec-18 22:17pm
   
got it! thanks a lot.

1 solution

Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 1

In integration test you do not need to get a real value of captcha but in UI /Real test you need to take it but not from mock framework.
   
Comments
Iris Shing 20-Dec-18 3:43am
   
Thanks for your reply.
But most of the apis needed to login to get token, otherwise they return 400, 403.
That's why I want to get the real value of the session.

So if I want to get the session, I should not use mock?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Cookies | Terms of Service
Web04 | 2.8.190424.1 | Last Updated 20 Dec 2018
Copyright © CodeProject, 1999-2019
All Rights Reserved.
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100