Unit testing help
Given the the following method what and how can I test?
p开发者_StackOverflow中文版ublic void DoSomething
{
Get Db record
Update Db the record
Log History in Db
Send an email notification
}
First of all, I agree with the other posters that this method is doing too much. I personally would have it do only the Db stuff, then have my application layer log the action and send the email. That said, to unit test this method, I would do the following (in C#):
Firstly, give the class that method exists in a constructor like this:
public MyClass(
IRepository repository,
ILoggingService loggingService,
INotificationClient notificationClient)
...where the IRepository
is an interface something like this:
interface IRepository
{
Db GetDbRecord();
void UpdateDbRecord(Db record);
}
...the ILoggingService
is something like this:
interface ILoggingService
{
void LogInformation(string information);
}
...and the INotificationClient
is something like this:
interface INotificationClient
{
void SendNotification(Db record);
}
In the constructor body, assign the passed-in parameters to private, readonly fields in MyClass
.
Next, in the DoSomething
method, get the Db
record from the IRepository
, update it and save it back to the IRepository
. Log the history using the ILoggingService
, then call SendNotification()
on the INotificationClient
.
Finally, in your unit tests, use a mocking framework (like Moq) to mock up one of each of the interfaces. Pass the mocked objects into a new instance of MyClass
, call DoSomething()
, then verify that your mocked IRepository
has been asked to update the Db
object, your mocked ILoggingService
has been asked to log a message, and your mocked INotificationClient
has been asked to SendNotification()
. That is to say:
Db record = new Db();
var mockRepository = new Mock<IRepository>();
mockRepository.Setup(r => r.GetDbRecord()).Returns(record);
var mockLoggingService = new Mock<ILoggingService>();
var mockNotificationClient = new Mock<INotificationClient>();
new MyClass(
mockRepository.Object,
mockLoggingService.Object,
mockNotificationClient.Object).DoSomething();
// NUnit syntax:
Assert.That(record["ExpectedUpdatedField"], Is.EqualTo("ExpectedUpdatedValue"));
mockRepository.Verify(r => r.UpdateDbRecord(record), Times.Exactly(1));
mockLoggingService.Verify(ls => ls.LogInformation(It.IsAny<string>()), Times.Exactly(1));
mockNotificationClient.Verify(nc => nc.SendNotification(record), Time.Exactly(1));
In the running system you would inject proper implementations of MyClass
' dependencies, and you've then shared the responsibilities amongst more coherent objects.
Bit long-winded, but that's how I'd do it :)
For Unit testing - you are not having a "unit" method. It is doing too many things. Extract out methods for each single job it does and test those methods.
Generally, Unit Tests do not include DB changes etc. so you have to see how you can attack that part. It is going to depend on the framework you use etc. You can mock the database methods and verify you are calling them. Don't go about seeing if the data is added to db etc. That is not Unit testing and you will start to realize that your tests become flaky, slow and cannot be parallelized etc.
Have integration tests / db test for the data part and make sure you rollback any data that you modify. Also make sure that no test is dependent on a change made by some other test.
精彩评论