Question

J'essaie de tester l'action de Index d'un contrôleur. L'action utilise AutoMapper pour mapper un domaine objet Customer à un modèle de vue TestCustomerForm. Bien que cela fonctionne, je suis préoccupé par la meilleure façon de tester les résultats que je reçois de l'action de Index.

Les regards d'action index du contrôleur comme ceci:

public ActionResult Index()
{
    TestCustomerForm cust = Mapper.Map<Customer,
        TestCustomerForm>(_repository.GetCustomerByLogin(CurrentUserLoginName));

    return View(cust);
}

Et ses regards TestMethod comme ceci:

[TestMethod]
public void IndexShouldReturnCustomerWithMachines()
{
    // arrange
    var customer = SetupCustomerForRepository(); // gets a boiler plate customer
    var testController = CreateTestController();

    // act
    ViewResult result = testController.Index() as ViewResult;

    // assert
    Assert.AreEqual(customer.MachineList.Count(),
        (result.ViewData.Model as TestCustomerForm).MachineList.Count());
}

Dans la méthode CreateTestController J'utilise Rhino.Mocks pour se moquer d'un référentiel client et le configurer pour retourner le client de SetupCustomerForRepository. De cette façon, je sais que le dépôt sera de retour le client prévu lorsque l'action Index appelle _repository.GetCustomerByLogin(CurrentUserLoginName). Par conséquent, je figure l'affirmation d'un nombre égal est suffisant pour satisfaire IndexShouldReturnCustomerWithMachines.

Tout cela dit que je suis préoccupé de ce que je testerai.

  1. Il semble présomptueux de jeter le result.ViewData.Model as TestCustomerForm. Est-ce vraiment un problème? Ce me préoccupe parce que, dans ce cas, je ne suis pas vraiment faisais le moteur du développement et essai, il semble que je compte sur une mise en œuvre particulière pour satisfaire au critère.
  2. Y at-il des tests plus appropriés pour assurer la cartographie correcte?
  3. Dois-je me tester chaque propriété mappée de la TestCustomerForm?
  4. Y at-il des tests d'action du contrôleur général plus que je devrais faire?
Était-ce utile?

La solution

Ceci est l'une des raisons pour lesquelles nous évoluons AutoMapper dans un ActionResult personnalisé ou un ActionFilter. À un certain moment, vous ne voulez vraiment tester que vous cartographié Foo à FooDto, mais pas nécessairement tester la cartographie réelle. En déplaçant AutoMapper dans les limites de la couche (par exemple entre le contrôleur d'une vue), vous pouvez tester simplement ce que vous racontez AutoMapper faire.

Ceci est similaire à tester un ViewResult. Vous ne testez pas d'un contrôleur vue a été rendue, mais que vous avez dit MVC de rendre tel ou tel point de vue. Notre résultat de l'action devient:

public class AutoMapViewResult : ActionResult
{
    public Type SourceType { get; private set; }
    public Type DestinationType { get; private set; }
    public ViewResult View { get; private set; }

    public AutoMapViewResult(Type sourceType, Type destinationType, ViewResult view)
    {
        SourceType = sourceType;
        DestinationType = destinationType;
        View = view;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var model = Mapper.Map(View.ViewData.Model, SourceType, DestinationType);

        View.ViewData.Model = model;

        View.ExecuteResult(context);
    }
}

Avec une méthode d'assistance sur une classe de contrôleur de base:

protected AutoMapViewResult AutoMapView<TDestination>(ViewResult viewResult)
{
    return new AutoMapViewResult(viewResult.ViewData.Model.GetType(), typeof(TDestination), viewResult);
}

Ce qui fait alors le contrôleur spécifie maintenant que ce à la carte depuis / vers, au lieu d'effectuer la mise en correspondance réelle:

public ActionResult Index(int minSessions = 0)
{
    var list = from conf in _repository.Query()
                where conf.SessionCount >= minSessions
                select conf;

    return AutoMapView<EventListModel[]>(View(list));
}

À ce stade, je ne ai besoin de tester, « assurez-vous que vous êtes la cartographie de cette objet Foo à ce type de FooDto de destination », sans avoir besoin d'effectuer effectivement la mise en correspondance.

EDIT:

Voici un exemple d'un extrait de test:

var actionResult = controller.Index();

actionResult.ShouldBeInstanceOf<AutoMapViewResult>();

var autoMapViewResult = (AutoMapViewResult) actionResult;

autoMapViewResult.DestinationType.ShouldEqual(typeof(EventListModel[]));
autoMapViewResult.View.ViewData.Model.ShouldEqual(queryResult);
autoMapViewResult.View.ViewName.ShouldEqual(string.Empty);

Autres conseils

I serait probablement séparer le couplage entre AutoMapper et le dispositif de commande par l'introduction d'une abstraction:

public interface IMapper<TSource, TDest>
{
    TDest Map(TSource source);
}

public CustomerToTestCustomerFormMapper: IMapper<Customer, TestCustomerForm>
{
    static CustomerToTestCustomerFormMapper()
    {
        // TODO: Configure the mapping rules here
    }

    public TestCustomerForm Map(Customer source)
    {
        return Mapper.Map<Customer, TestCustomerForm>(source);
    }
}

Ensuite, vous passer ce dans le contrôleur:

public HomeController: Controller
{
    private readonly IMapper<Customer, TestCustomerForm> _customerMapper;
    public HomeController(IMapper<Customer, TestCustomerForm> customerMapper)
    {
        _customerMapper = customerMapper;
    }

    public ActionResult Index()
    {
        TestCustomerForm cust = _customerMapper.Map(
            _repository.GetCustomerByLogin(CurrentUserLoginName)
        );
        return View(cust);
    }
}

Et dans votre test unitaire vous utiliseriez votre cadre moqueur favori pour ce stub mappeur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top