Until recently, I would have just pointed you at plenty of other existing questions that say that you generally shouldn't need access to any private stuff, in order to write unit tests - as the whole point of tests is to test the public (and protected) interface.
For example, in the question how to unit-test private methods in jquery plugins?, there is this answer:
The same applies here as with any other language and testing privates: To test private methods, you should exercise them via the public interface. In other words, by calling your public methods, the private methods get tested in the process because the public methods rely on the privates.
Generally private methods are not tested separately from the public interface - the entire point is that they are implementation details, and tests should generally not know too much about the specifics of the implementation
In your specific case, you say:
Since the data is private, I can only add things via the public interface of the object. This run codes that need not be run during a unit test and in some case is just a copy and paste from another test.
I would be cautious about having any tests that avoided code that normally needs to be run - for fear that your tests would spuriously pass, due to some difference between the test behaviour and the real world.
Having said all that, I recently learned about a really excellent article on writing testsuite called The Way of Testivus. It's a PDF that's only 12 pages long, and a really easy and enjoyable read.
What's relevant here is the section entitled "Sometimes, the test justifies the means" - in the context of violating encapsulation to enable testing. I highly recommend it.
(Another favourite of mine is "An ugly test is better than no test.")
So, in your context, if you still decide you really want a different way to set up data purely for tests, it might be reasonable to add a public setter method, specifically documented as only existing to aid writing of tests.
TL;DR:
- Write the test
- If the test does too much, the code may do too much too.
- It may not be a unit test (but not a bad test).
The first thing for testing is about dogma being unhelpful. I enjoy reading The Way of Testivus which points out some issues with dogma in a lighthearted way.
Write the test that needs to be written.
If the the test needs to be written some way, write it that way. Attempting to force the test into some idealized test layout or not have it at all is not a good thing. Having a test today that tests it is better than having a "perfect" test some later day.
I will also point to the bit on the ugly test:
When the code is ugly, the tests may be ugly.
You don’t like to write ugly tests, but ugly code needs testing the most.
Don’t let ugly code stop you from writing tests, but let ugly code stop you from writing more of it.
These can be considered truisms to those who have been following for a long time... and they just become ingrained in the way of thinking and writing tests. For people that haven't been and are trying to get to that point, reminders can be helpful (I even find re-reading them helps me avoid getting locked into some dogma).
Do consider that when writing an ugly test, if the code it may be an indication that the code is trying to do too much too. If the code that you are testing is too complex to be properly exercised by writing a simple test, you might want to consider breaking the code into smaller parts that can be tested with the simpler tests. One should not write a unit test that does everything (it might not be a unit test then). Just as 'god objects' are bad, 'god unit tests' are bad too and should be indications to go back and look at the code again.
You should be able to exercise all the code with reasonable coverage through such simple tests. Tests that do more end to end testing that deal with larger questions ("I have this object, marshalled into xml, sent to the web service, through the rules, back out, and unmarshalled") is an excellent test - but it certainly isn't a unit test (and falls into the integration testing realm - even if it has mocked services that it calls and custom in memory databases to do the testing). It may still use the XUnit framework for testing, but the testing framework doesn't make it a unit test.
Best Answer
Dependency Injection and mocking is your friend. Whenever you find something hard to test, it usually means your class is having too many responsibilities. I'm assuming you are sending your emails using some library. That library most likely takes your message info (destination address, subject, body). What you want to do, is decouple your business logic that's in charge of collecting all email addresses, email subject and body, from that 3rd party library. How? By abstracting that library using an interface, and then injecting it into your Email class.
Now in your production code, you would create a real EmailLibrary like this:
and then inject it into your Email class, like this:
And if you wanted to test your business logic, all you need to do in your test code, is inject either a mock, or your own stub that implements an IEmailLibrary interface. For example:
You inject it in your test like this:
I recommend using mocking frameworks, as opposed to my stub example (like Moq for C#), which make it really easy to verify your injected dependencies were called with the right arguments.