I'm new to TDD and ATDD, and I'm seeking to understand the connection between a user story and its acceptance criteria. For context, I'm building a 3-tier application in C#, with an MVC front-end.
Say, for instance, that I have the following user story:
In order to ensure Capacity Data is entered correctly As a person updating this data I want feedback when the data entered doesn't conform to our business rules.
It makes sense to me to break this down and define what "Capacity Data" is, and the business rules that govern this.
For instance, maybe it has a "Number of Machines" property that has to be greater than zero.
What I want to avoid doing is testing the framework--and if I follow correctly, what I want to do is test that this business logic is correctly implemented, i.e. that:
- Business rules ("Number of machines must be greater than zero", and others) are correctly implemented in the codebase.
- If a business rule is violated, the user is alerted of this mistake.
I believe I could test rule #2 by validating that invalid model state in the controller redirects to the same page, for instance--and there's TONS of examples of that.
However, doesn't doing this require putting decorations on the viewmodel--and that ultimately this implements the business rule from the perspective of the user? (thus satisfying #1?)
Let's say I have the following sort-of statement/unit-test:
[Test]
public void GivenCapacityModelWhenNumMachinesZeroOrLessThenModelShouldBeInvalid()
{
// Given
IValidatorThing validator = new ValidatorThing(); //What enforces the rule? Should this be a controller service? Or a decorator such as [Range(0.000001,1000000)]? Doesn't each require different testing methods?
var invalidModel = new CapacityModel(); // Or the viewmodel?
double zeroValue = 0.000001;
invalidModel.NumMachines = zeroValue;
// When
var modelIsValid = ValidatorThing.validateModel(invalidModel);
// Then
Assert.IsFalse(modelIsValid);
}
The above won't compile, of course. I've left out any particular mocking or fixturing framework out for now, to keep it simple. So, to make this test at least compile (but still fail), I have some decisions to make:
- Is CapacityModel supposed to be a viewmodel? Or a DTO from the service layer? Or a metadata class in the DAL layer? I can implement either and make the test pass...but what should I really be testing?
- Is the "validator" checking the behavior of a service that validates this model property? Or data annotations on the CapacityModel? Again, what should I really be testing in the context of a 3-tier application?
Some things to consider: One thing I will know is that the database tables will have constraints that describe these rules--so it would seem that the purpose of this rule is really to communicate these rules to whoever is using the application. In that case, could I safely assume it would violate DRY to have have the rules appear in three places: the viewmodel, data entity, and database tables.
The reason we have these rules in the database is because we want to ensure if a DBA needs to mess with the records that the rules aren't accidentally violated. However, to my knowledge there isn't a great way to translate those CONSTRAINT rules up to the DAL of the application...so I suppose they would need to be repeated at least one more time in the application for sake of communicating them to the user.
So, if I were to write a unit test to fulfill the business rule wouldn't I be writing only to ensure the rules mirror the database? And separately, also writing a unit test that ensures the correct message is displayed to the user?
Any guidance you can offer would be wonderful. I want to feel that the decisions I've made were reasonable, or at least have an idea of alternative ways to solve the problem better.
Thanks,
Lawrence
EDIT:
So, ultimately I was trying to drive at a centralized way of managing validation in the application so that I could have separation of concerns--i.e., that the controller only cared about routing, the viewmodels only cared about displaying data, validators only cared about validation, etc...as opposed to having validation in the viewmodel, for instance.
I found a very helpful article that helped me to grasp how to do this, using the existing MVC infrastructure. Hopefully this will help others looking into these types of scenarios.