is it practically possible to do good TDD (or BDD) without using any DI framework in java?
IMO one of the main characteristics of a good TDD is: testing your class (or actua开发者_如何学JAVAlly unit) in isolation.
When you do so, you are able to actually test single behavior in each test -- only one test will fall for a single problem you have.
For this you first must verify that there are no static references from your class (including constructors AKA new
keyword).
in theory it is easy not to use any dependency injection framework and test your classes in complete isolation, for this you need to inject all dependencies in the constructor and create Factories classes that will call the new
keyword.
I found this theoretical MO to be too hard to actually do in practice.
Am I missing something important in the process?
EDIT: I do think that we should all aim to one failure for one code change. it will never be perfect but it will get your code closer to there, people forget that test code is also maintainable code, specification of tested code changes.
If you don't aim there you will end up deleting tests which is a poor solution
I don't think you need DI frameworks for unit testing - TDD has been around years before DI became a fashion. And in case of code which does not use an explicit DI framework itself, why would you use that for unit tests?
Sure, DI frameworks can simplify the task to some extent. OTOH they do it by putting the complexity elsewhere. My preference is to have self-containing unit tests, and so far I have almost always managed without a DI framework. That often requires refactoring my test code, and sometimes even duplicating code, but I can live with that. And, as @kostja noted, mocking frameworks are a big help too.
Designing for testability is important too - if a class is hard to test per se, it is better to refactor it rather than trying to alleviate the pain with a DI framework.
Note that all this requires lots of practice, as well as a change in mindset and ways of thinking. All of which takes time and patience... the more you keep up, the better you become :-)
you are able to actually test single behavior in each test -- only one test will fall for a single problem you have.
I think your conclusion is wrong here. Even if you test a single thing in each test, you can break a lot of tests with a single code change. Update: Every test is to verify a single path of execution within the tested code. However, those paths of execution are rarely independent - there tend to be common path sections in all but the simplest methods. Any change there can break multiple tests.
DI is supposed to reduce direct dependencies, actually simplifying mocking and unit testing. Maybe you are missing the mock part? this post could clarify things.
Sure, it is possible. And easy. For example, lets say you want to test the following method, which calls static methods on a Database
class (a static persistence facade) and instantiates SimpleEmail
(from Apache Commons Email) directly:
public void doBusinessOperationXyz(EntityX data) throws EmailException
{
List<EntityX> items =
Database.find("select x from EntityX x where x.xyz=?", data.getXyz());
BigDecimal total = ...compute/obtain the value...
data.setTotal(total);
Database.persist(data);
Email email = new SimpleEmail();
email.setSubject("Notification about processing of ...");
email.addTo(data.getCustomerEmail());
email.setMsg("...some notification message...");
email.send();
}
This method can be tested in isolation from the Database
and Email
dependencies with the following unit test, using JMockit:
@Test
public void doBusinessOperationXyz() throws Exception
{
final EntityX data = new EntityX(5, "abc", "5453-1");
final List<EntityX> items = new ArrayList();
items.add(new EntityX(1, "AX5", "someone@somewhere.com"));
new Expectations()
{
@Mocked final Database unused = null;
@NonStrict SimpleEmail email;
{
Database.find(withSubstring("select"), (Object[]) any); result = items;
Database.persist(data);
email.send(); times = 1;
}
};
new MyBusinessService().doBusinessOperationXyz(data);
}
Normally, it would be necessary to inject mock objects for the Database
and Email
dependencies, forcing a more complex solution. By mocking/stubbing static methods and constructors, we can keep a simple, natural, and elegant design instead.
精彩评论