My application has the following project structure: There is the Business Logic project and the UnitTesting project where the methods from the Business Logic are tested. No mocking or testing framework are used (we rely on Visual Studio unit tests and we implement our own mock objects). In the Business Logic let's say I have the following method:
public static void SomeMethod ()
{
....
if (cond1)
if (cond2)
SendMail();
}
I want to unit test that. I don't want to unit test the sending of the mail, rather the mail is sent under the correct circumstan开发者_JAVA技巧ces. So I was thinking to do something like
public class MailSender : ISmtpMail
{
// stuff
}
public class FakeMailSender : ISmtpMail
{
//
static bool SendMail ()
{
return true;
}
}
The problem is that I don't know how to enforce the usage of the FakeMailSender in the unit test project or in the unit test methods which look something like :
[TestMethod]
public static void SomeMethod_Test()
{
// some mock initialization
BusinessLogic.SomeMEthod ();
// checks
}
without changing the BusinessLogic method signature or injecting code (which is undesired)
It's not clear where your first method "lives". It seems to me that:
- Your
SendMail
method shouldn't be static, which makes all kinds of test double injection tricky to say the least - You should inject
ISmtpMail
into whatever needs to send mail.
You say you want to do it "without injecting code" - why not? The "mail sending" service is clearly a dependency: your code will be clearer, less tightly coupled, and easier to test if you inject that depedency, whether explictly or with the help of a DI container.
The problem is that you're using static
methods. There's no clean way of configuring that singleton instance to use different services.
Consider this. Make your SomeMethod
non-static and inject the required services into the business logic instance:
private ISmtpMail _smtpMail;
public BusinessLogic(ISmtpMail smtpMail)
{
_smtpMail = smtpMail;
}
public void SomeMethod()
{
...
_smtpMail.Send();
}
Your test code will then be able to enforce which service to use:
[TestMethod]
public static void SomeMethod_Test()
{
// some mock initialization
var bl = new BusinessLogic(new FakeMailSender());
bl.SomeMethod();
// checks
}
For production code, I strongly encourage you to look at some of the excellent Dependency Injection frameworks out there, e.g. Autofac, which makes the service injections happen more or less by magic.
With statics you can use commercial library like Typemock. However, if you are forced to use that it means you have a bad design, probably even misunderstanding the concept of oop. I'd recommend to refactor and use the injection as Jon Skeet and Peter Lillevold already suggested.
First of all you should avoid using any static mocking, and use classical injection pattern as already suggested.
But if you really want to mock static methods (for example, in cases when you can't change this code because this code reside in third-party library), you can use Moles.
Suppose you have following MailSender class:
namespace MyNamespace {
class MailSender
{
public static bool SendMail() {...}
}
}
Moles can generate "mole" for this class with Action delegate witch would be called instead of original method every time your code calls this original method. And than you could write following test:
[TestMethod]
public static void SomeMethod_Test()
{
// After that all calls to MyNamespace.MailSender.SendMail whould
// return true
MyNamespace.Moles.MMailSender.SendMail = () => true;
BusinessLogicClass.SomeMethod();
}
BTW, Moles can also help you mocking not only static methods and non-virtual methods with moles, but this tool can generate similar stub classes for testing interfaces or classes with virtual methods.
精彩评论