Click here to Skip to main content
15,879,535 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
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:
C#
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:
C#
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:
C#
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
C#
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
C#
[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?
C#
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.

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900