Object instantiation in Test Classes
I'm using MS UnitTesting and trying to find my way around writing my first unit tests. It seems like all my unit tests start off with creating the same few objects...
[TestMethod]
CanCreateOrder()
{
<create an order>
...
}
[TestMethod]
CanSetOrderDeliveryAddress()
{
<create an order>
<create an address>
order.DeliveryAddress = address;
...
}
[TestMethod]
CanDispatchAnOrder()
{
<create an order>
<create an address>
order.DeliveryAddress = address;
order.Dispatch();
...
}
...etc
Is this normal, or have I got the wrong idea? I 开发者_如何转开发kind of thought every test was supposed to be independent, but how would it be possible to test Dispatch()
without that implicitly relying on CreateOrder
and SetDeliveryAddress
already passing?
And the second part of the question, if the above approach looks ok, should I be using a factory or something to instantiate these objects in my test project? I'm not sure if a test project should only contain test classes/methods, or it's ok to add a bunch of helpers in there too.
You seem to be on the right track to me. In a lot of unit tests you will need to set up the state of the object you are testing in order to be able to test a particular part of behaviour. It will help to group tests in classes when the bahaviour you are testing requires similar setup, and to use [TestInitialize] methods to reduce the duplication of that setup.
eg:
[TestClass]
public class WhenReadyToDispatch{
private Order order;
[TestInitialize]
public void Initialize
{
order = <create an order>
order.DeliveryAddress = <create an address>
}
[TestMethod]
CanChangeOrderDeliveryAddress()
{
order.DeliveryAddress = address;
}
[TestMethod]
CanDispatchAnOrder()
{
order.Dispatch();
}
}
It's fine to have helper classes in the test project - you should be aiming to make you test code well factored as your production code.
Your first question is to do with mocking and stubbing where you create a fake order and address created from the interface of each class. This is how you can then just be testing the Dispatch method.
You can then assert that the dispatch method did the right thing by checking what happened to your fake (stub/mock) objects.
In answer to the second part of your question; It's a very good idea to have factory methods and even class hierarchies in order to make writing the tests easier. It's just as important to structure tests in a good way, as it is to structure production code.
I think part of your problem may be in the design of your order object. Trying to write a free standing test only to find that it relies on other functions generally suggest that they are not adequately decoupled. A couple of rules of thumb that may be appropriate here:
If Order.DeliveryAddress is just a simple getter/setter then don't worry about testing it. Thats like trying to prove that C# behaves as it should. There is little advantage to doing this. Conversely, having your dispatcher test rely on this property being in working order is not really a dependency.
However if Order.DeliveryAddress is performing logic, such as ensuring that the address is only modifiable for non-dispatched orders for example, then it is more complicated. You probably don't want to try to dispatch an entire order just to test that Order.DeliveryAddress is no longer modifiable afterwards.
Invoking Single Responsibility Principle (See 1 and 2) here would say that the Order class is now doing too much. It is both dispatching orders and enforcing object state integrity of the order data. In which case you probably want to split the dispatching functionality out into a DispatcherService that simply takes an order and dispatches it, setting an IsDispatched flag on the order in the process.
You can then test the DeliveryAddress behavior by just setting the IsDispatched property appropriately.
A third approach (which is sort of cheating but works well in situations where you are trying to get some testing over legacy objects) is to subclass Order to create a TestableOrder class that exposes to the test fixture the ability to tinker with the internal state of the class. In other words, it could expose a MarkAsDispatched() method that would set the classes internal IsDispatched flag and thus allow you to test that DeliveryAddress is only settable prior to being marked as dispatched.
Hope that helps.
精彩评论