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 });
}
var now = DateTime.UtcNow;
JwtHelper jwtHelper = new JwtHelper(dal, _options);
var encodedJwt = jwtHelper.GenerateToken(acc, model.remember);
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.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>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSession();
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>();
var mockRepo = new Mock<ISessionWapper>();
var controller = new CommonApiController(mockRepo.Object, logger);
var result = await controller.GetCaptcha();
var captcha =
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;
Client = _server.CreateClient();
Client.BaseAddress = new Uri(baseAddress);
Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); ;
}
public void Dispose()
{
_server?.Dispose();
Client?.Dispose();
}
}
Thank you!