Search code examples
c#unit-testingintegration-testingmediatr.net-4.7

How to do Integration Tests with Mediatr on .net framework 4.7?


I'm using the Mediatr library to register and call my RequestHandlers. Everything went fine until I started reading more about integrated tests.

PLEASE READ AFTER EDIT

I can't call my class which inherits from the RequesHandler.

My class looks like this:

public class MyRequestHandler : RequestHandler<MyRequest, MyResponse>
{
  ....
}

I'm not using the Meditr async and I'm using .net framework 4.7 instead of asp.net core, so, everything looks like returns me answers for asp.net core.

When I construct MyTestClass, to construct the RequestHandler I have to create a ServiceFactory and maybe this is the problem because I don't know how.

 public MyClassTest()
 {
    ServiceFactory sv = null;
    _mediator = new Mediator(sv);
 }

EDIT

Providing more info

I have this Handler in my Application Layer

public class LogInUserByFormHandler : RequestHandler<LogInUserByFormRequest, LogInUserByFormResponse>
{
    private readonly IValidator<LogInUserByFormRequest> _validator;

    public LogInUserByFormHandler(IValidator<LogInUserByFormRequest> validator)
    {
        _validator = validator;
    }

    protected override LogInUserByFormResponse Handle(LogInUserByFormRequest request)
    {
        _validator.ValidateAndThrow(request);

        var userInfo = GetUserInfo(request);
        ValidateLogInUserByFormRules(userInfo);

        var userLoginInfo = GetValidUserLoginInfo(request);
        ValidateUserLoginInfoByFormRules(userLoginInfo);

        var sessionKey = CreateUserSessionKey(userInfo);

        var response = new LogInUserByFormResponse
        {
            UserName = request.UserName,
            SessionKey = sessionKey,
            UserId = userInfo.id_usuario
        };
        return response;
    }

    //A LOT OF CODE HERE, methods and etc
}

As it's possible to see, it implements the Mediatr. On my Web Project on Presentation Layer, I used AutoFac to Inject the Handlers, so, any Request I do is always handled by the right method. All I have to do is call, like this:

var logInByFormRequest = new LogInUserByFormRequest
{
    UserName = viewModel.UserName,
    Password = viewModel.Password
};

var response = _mediator.Send(logInByFormRequest).Result;

This works like a charm. The problem now is on the Test project. It references the Application as the Presentation Project does. I don't know how to make the mediator.send find the right method.

EDIT²

Here comes my test code

[TestClass]
public class LogInUserByFormTest 
{
    private LogInUserByFormRequest CreateRequest(string userName, string password)
    {
        LogInUserByFormRequest request = new LogInUserByFormRequest
        {
            UserName = userName,
            Password = password
        };

        return request;
    }

    [TestMethod]
    [Description("")]
    public void UserName_ShouldHave_Max_30Characters_Exception()
    {
        try
        {
            var request = CreateRequest("UserNameIsGreaterThanAllowed", "password");
            var mediator = new Mock<IMediator>();
            var response = mediator.Object.Send(request).Result;
        }
        catch (System.Exception ex)
        {

            throw;
        }
    }
}

The result (response) is always null and the mediator doesn't call the right handler.

EDIT3

Here is how I register the handlers and validators. I use autofac. This class here is called on the global.asax

 public class AutofacConfig
{
    public static void ConfigureContainer()
    {
        var builder = new ContainerBuilder();

        builder.RegisterControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
        builder.RegisterType<Mediator>().As<IMediator>().InstancePerLifetimeScope();
        builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();
        builder.RegisterType<FluentValidationModelValidatorProvider>().As<ModelValidatorProvider>();
        builder.RegisterType<RegistryManagerService>().As<IRegistryManagerService>().SingleInstance().WithParameter("appName", ConfigurationManager.AppSettings["APPNAME"]);

        builder.Register<ServiceFactory>(context =>
        {
            var c = context.Resolve<IComponentContext>();
            return t => c.Resolve(t);
        });

        builder.RegisterAssemblyTypes(Assembly.Load("Docspider.Application"))
            .Where(x => x.Name.EndsWith("Handler"))
            .AsImplementedInterfaces();

        builder.RegisterAssemblyTypes(Assembly.Load("Docspider.Application"))
           .Where(x => x.Name.EndsWith("Validator"))
           .AsImplementedInterfaces()
           .InstancePerLifetimeScope();

        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

public class AutofacValidatorFactory : ValidatorFactoryBase
{
    private readonly IComponentContext _context;

    public AutofacValidatorFactory(IComponentContext context)
    {
        _context = context;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        if (_context.TryResolve(validatorType, out object instance))
        {
            var validator = instance as IValidator;
            return validator;
        }

        return null;
    }
}

Solution

  • For such an integration test you would need to configure the necessary dependencies. Since you have indicated that Autofac is being used then configure a container just as you would have in production. Use the container to get the mediator and perform the desired test.

    For example.

    [TestClass]
    public class LogInUserByForm_IntegrartionTest {
        private LogInUserByFormRequest CreateRequest(string userName, string password) {
            LogInUserByFormRequest request = new LogInUserByFormRequest {
                UserName = userName,
                Password = password
            };
    
            return request;
        }
    
        IMediator BuildMediator() {
            //AutoFac
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();
    
            var mediatrOpenTypes = new[] {
                typeof(IRequestHandler<,>)
            };
    
            foreach (var mediatrOpenType in mediatrOpenTypes) {
                builder
                    .RegisterAssemblyTypes(typeof(LogInUserByFormRequest).GetTypeInfo().Assembly)
                    .AsClosedTypesOf(mediatrOpenType)
                    .AsImplementedInterfaces();
            }
    
            builder.Register<ServiceFactory>(ctx => {
                var c = ctx.Resolve<IComponentContext>();
                return t => c.Resolve(t);
            });
    
            //...all other needed dependencies.
    
            //...
    
            var container = builder.Build();
    
            var mediator = container.Resolve<IMediator>();
            return mediator;
        }
    
        [TestMethod]
        [Description("")]
        public async Task UserName_ShouldHave_Max_30Characters_Exception() {
            try
            {
                //Arrange
                var request = CreateRequest("UserNameIsGreaterThanAllowed", "password");
                var mediator = BuildMediator();
    
                //Act
                var response = await mediator.Send(request);
    
                //Assert
                //...assert the expected values of response.
            }
            catch (System.Exception ex)
            {
    
                throw;
            }
        }
    }
    

    The above was modeled after the examples provided by MediatR.Examples.Autofac