Hi Vladimir.
How to test (integration test) a CommandHandler when it is internal ?
1)We can make it public (just for the matter of testing!!!!).
2)Or we can test controller's action methods. If we test controller's method then our tests cover the Messages too (it is good), but on the other hand, we get involved in working with Json in tests and we know that output json format is changed frequently. Besides that Messages uses an IServiceProvider and it is hard to create an instance of it in tests.
What do you suggest?
public sealed class EnrollCommand : ICommand
{
public long Id { get; }
public string Course { get; }
public string Grade { get; }
public EnrollCommand(long id, string course, string grade)
{
Id = id;
Course = course;
Grade = grade;
}
internal sealed class EnrollCommandHandler : ICommandHandler<EnrollCommand>
{
private readonly SessionFactory _sessionFactory;
public EnrollCommandHandler(SessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
public Result Handle(EnrollCommand command)
{
var unitOfWork = new UnitOfWork(_sessionFactory);
var courseRepository = new CourseRepository(unitOfWork);
var studentRepository = new StudentRepository(unitOfWork);
Student student = studentRepository.GetById(command.Id);
if (student == null)
return Result.Fail($"No student found with Id '{command.Id}'");
Course course = courseRepository.GetByName(command.Course);
if (course == null)
return Result.Fail($"Course is incorrect: '{command.Course}'");
bool success = Enum.TryParse(command.Grade, out Grade grade);
if (!success)
return Result.Fail($"Grade is incorrect: '{command.Grade}'");
student.Enroll(course, grade);
unitOfWork.Commit();
return Result.Ok();
}
}
}
public sealed class StudentController : BaseController
{
private readonly Messages _messages;
public StudentController(Messages messages)
{
_messages = messages;
}
[HttpPost("{id}/enrollments")]
public IActionResult Enroll(long id, [FromBody] StudentEnrollmentDto dto)
{
Result result = _messages.Dispatch(new EnrollCommand(id, dto.Course, dto.Grade));
return FromResult(result);
}
}
public sealed class Messages
{
private readonly IServiceProvider _provider;
public Messages(IServiceProvider provider)
{
_provider = provider;
}
public Result Dispatch(ICommand command)
{
Type type = typeof(ICommandHandler<>);
Type[] typeArgs = { command.GetType() };
Type handlerType = type.MakeGenericType(typeArgs);
dynamic handler = _provider.GetService(handlerType);
Result result = handler.Handle((dynamic)command);
return result;
}
public T Dispatch<T>(IQuery<T> query)
{
Type type = typeof(IQueryHandler<,>);
Type[] typeArgs = { query.GetType(), typeof(T) };
Type handlerType = type.MakeGenericType(typeArgs);
dynamic handler = _provider.GetService(handlerType);
T result = handler.Handle((dynamic)query);
return result;
}
}
Hi Vladimir.
How to test (integration test) a CommandHandler when it is internal ?
1)We can make it public (just for the matter of testing!!!!).
2)Or we can test controller's action methods. If we test controller's method then our tests cover the
Messagestoo (it is good), but on the other hand, we get involved in working with Json in tests and we know that output json format is changed frequently. Besides thatMessagesuses an IServiceProvider and it is hard to create an instance of it in tests.What do you suggest?