Question

Quick terminology question that's somewhat related to my main question: What is the correct term for a model class and the term for a instance of that class?

I am learning Test Driven Development, and want to make sure I am learning it the right so I can form good habits.

My current project has a SalesmanController, which is pretty much a basic resource controller. Here's my current issue (I can get it working, but I want to make sure its done as "right" as possible)

I have a 'Salesman' model.

The 'Salesman' is mapped as having many 'Sales' using my ORM.

The 'Sales' model is mapped as belongsTo 'Salesman' using my ORM.

I have created a ORMSalesmanRepository which implements the SalesmanRepositoryInterface.

My controller has SalesmanRepositoryInterface passed to it upon construction(constructor dependency injection).

My controller calls the find method on the SalesmanRepositoryInterface implementation it has been given to find the correct salesman.

My view needs information on the salesman and information on all 'Sales' records that belong to him.

The current implementation of SalesmanRepositoryInterface returns an instance of a ORMRecord, and then passes that to the view, which retrieves the sales from the ORMRecord.

My gut tells me this implementation is wrong. The orm record implements Array Access, so it still behaves like an array as far as the view knows.

However, when trying to go back and implement my unit tests I am running into issues mocking my dependencies. (I now know with TDD I'm supposed to make my unit tests and then develop the actual implementation, didn't figure this out till recently).

Salesman = MockedSalesman;

SalesRecords = MockedSalesman->Sales;

Is it poor programming to expect my Salesman to return a ORMObject for the controller to use (for chaining relationships maybe?) or is a controller becoming to 'fat' if I'm allowing it to call more than just basic get methods (using arrayaccess []) on the ORMObject? Should the controller just assume that whatever it gets back is an array (or at least acts like one?)

Also, should it ever come up where something one of my mocked classes returns needs to be mocked again?

Thanks in advance everybody.

Was it helpful?

Solution

What is the correct term for a model class and the term for a instance of that class?

Depends on what you mean with "model classes"? Technically model is a layer, that contains several groups of classes. Most notable ones would be: mappers, services an domain objects. Domain objects as whole are implementation of accumulated knowledge about business requirements, insight from specialists and project goals. This knowledge is referred to as "domain model".

Basically, there is no such thing as "model class". There are classes that are part of model.

What is the controller allowed to assume about what it recieves from a service?

Nothing, because controller should not receive anything from model layer. The responsibility of controller is to alter the state of model layer (and in rare cases - state of current view).

Controller is NOT RESPONSIBLE for:

  • gather data from model layer,
  • initializing views
  • passing data from model layer to views
  • dealing with authorization checks

The current implementation of SalesmanRepositoryInterface returns an instance of a ORMRecord, and then passes that to the view, which retrieves the sales from the ORMRecord.

It sounds like you are implementing active record pattern. It has very limited use-case, where is is appropriate to use AR - when object mostly consists of getters and setters tht are directly stored in a single table. For anything beyond that active record becomes an anti-pattern because it violates SRP and you loose the ability to test your domain logic without database.

Also, are repository should be returning an instance of domain object and makes sure that you are not retrieving data repeatedly. Repositories are not factories for active record instances.

Is it poor programming to expect my Salesman to return a ORMObject for the controller to use (for chaining relationships maybe?) or is a controller becoming to 'fat' if I'm allowing it to call more than just basic get methods (using arrayaccess []) on the ORMObject?

Yes, it's bad code. Your application logic (one that would usually be contained in services) is leaking in the presentation layer.

OTHER TIPS

At this stage I wouldn't stress too much about your implementation - if you try and write proper tests, you'll quickly find out what works and what doesn't.

The trick is to think hard about what each component is trying to achieve. What is your controller method supposed to do? Most likely it is intended to create a ViewModel of some kind, and then choose which View to render. So there's a few tests right there:

  • When I call my controller method with given arguments (ShowSalesmanDetail(5))
  • It should pick the correct View to render ('ShowSalesmanDetail')
  • It should construct the ViewModel that I expect (A Salesman object with some Sales)

In theory, at this point you don't care how the controller constructs the model, only that it does. In practice though you do need to care, because the controller has dependencies (the big one being the database), which you need to cater for. You've chosen to abstract this with a Repository class that talks to an ORM, but that shouldn't impact the purpose of your tests (though it will definitely alter how you implement those tests).

Ideally the Salesman object in your example would be a regular class, with no dependencies of its own. This way, your repository can construct a Salesman object by populating it from the database/ORM, and your unit tests can also use Salesman objects that you've populated yourself with test data. You shouldn't need to mock your models or data classes.

My personal preference is that you don't take your 'data entities' (what you get back from your ORM) and put them into Views. I would construct a ViewModel class that is tied to one View, and then map from your data entities to your ViewModel. In your example, you might have a Salesman class which represents the data in the database, then a SalesmanModel which represents the information you are displaying on the page (which is usually a subset of what's in the db).

So you might end up with a unit test looking something like this:

public void CallingShowSalesmanShouldReturnAValidModel()
{
    ISalesmanRepository repository = A.Fake<ISalesmanRepository>();
    SalesmanController controller = new SalesmanController(repository);

    const int salesmanId = 5;

    Salesman salesman = new Salesman
    {
        Id = salesmanId,
        Name = "Joe Bloggs",
        Address = "123 Sesame Street",
        Sales = new[]
        {
            new Sale { OrderId = 123, SaleDate = DateTime.Now.AddMonths(-1) }
        }
    };

    A.CallTo(() => repository.Find(salesmanId)).Returns(salesman);

    ViewResult result = controller.ShowSalesman(salesmanId) as ViewResult;
    SalesmanModel model = result.Model as SalesmanModel;

    Assert.AreEqual(salesman.Id, model.Id);
    Assert.AreEqual(salesman.Name, model.Name);

    SaleModel saleModel = model.Sales.First();
    Assert.AreEqual(salesman.Sales.First().OrderId, saleModel.OrderId);
}

This test is by no means ideal but hopefully gives you an idea of the structure. For reference, the A.Fake<> and A.CallTo() stuff is from FakeItEasy, you could replace that with your mocking framework of choice.

If you were doing proper TDD, you wouldn't have started with the Repository - you'd have written your Controller tests, probably got them passing, and then realised that having all this ORM/DB code in the controller is a bad thing, and refactored it out. The same approach should be taken for the Repository itself and so on down the dependency chain, until you run out of things that you can mock (the ORM layer most likely).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top