TDD and Test Data

2019-05-23 08:28发布

问题:

I'm new to TDD and was wondering where to begin. I've read about as much as I think I can without throwing up and am still confused what to test and what not to test. For example, I know, from reading, we should not run tests against databases, thus enters the mocking framework. We mock our repositories so they will return fake data. My question is do we test requirements for data constants? For example, a requirement may state, a person should always have a name, thus:

Assert.IsNotNull(personObject.Name);

Should always be true, but how do I test it without have "fake" data? Do I care to test that type of requirement?

回答1:

Let's take your requirement "a person should always have a name". Where could we start?

First, we need some clarity. I believe that when you say "should always have a name", you mean "should never have a null or empty string" as a name.

What we probably really mean here is "when a person is stored in our database, its name cannot be null or empty". The first line of attack would be to enforce that with a constraint in your database; however, nothing protects you against a rogue DBA removing that constraint, so you may want to test that what you think is true about the system, will be flagged by a failing test if it changes. For this purpose you would write a test like "when my app sends a Person with a Null name to be saved to the DB, it should fail miserably". That's not a unit test, it is more of an integration test - and it is more complicated to write than a good old unit test.

However, this still doesn't cover the scenario of a rogue DBA removing the constraint and directly creating records which have null names. In other words, your app cannot trust the data it gets back to be correct, so the question becomes: how do you want your domain to deal with the possibility of persons with null names?

A very literal approach could be to enforce that Person can only be constructed with non-null Name, and throws otherwise. That would be easy to unit test and enforce, but will probably make development painful. A more pleasant approach would be to have no constraints on Person, but a Validator class, which can Validate a Person for broken rules. This is more palatable because you could now do anything you wish to a Person (figuratively) and then Validate whether that Person is still in a Valid state.

This has the benefit of

1) being very easily testable: creating a Unit Test for such a validator is a piece of cake,

2) addressing the rogue DBA issue: you can now Validate everything that comes from or goes to the outside of the app, by applying the Validator.

So, being the lazy developer that I am, this is where I would start: go with the Validator, because it addresses my problem, while being much easier to test than something involving the data. In other words, in general I tend to stick as much as possible with Unit Tests (i.e. completely in memory), and to have as much as I can of my business logic in the code / domain, because it's easier to have all in one place, and easier to test.



回答2:

You can use acceptance or integration testing to cover your database and constraints.

For instance, on one project, we had a separate "integration" package with tests which just checked our NHibernate bindings. If you're not using NHibernate, you could just do this with your repositories, keeping the database connections in place. You can verify constraints, referential integrity, etc. from this level.

If you're still looking for guidance on other aspects of TDD, try Dan North's post on BDD, which starts:

"I had a problem... programmers wanted to know where to start, what to test and what not to test, how much to test in one go, what to call their tests, and how to understand why a test fails."