2
votes

I am new to unit testing. I am trying to test something very simple:

[HttpPost]
public ActionResult EditProfile(ProfileViewModel model)
{
    if (ModelState.IsValid)
    {
        // Retrieve current user
        var userId = User.Identity.GetUserId();
        var user = _dataRepository.GetUserById(userId);

        //If it isn't the single-instance default picture, delete the current profile
        // picture from the Profile_Pictures folder
        if (!String.Equals(user.ProfilePictureUrl, _defaultPic))
            System.IO.File.Delete(Server.MapPath(user.ProfilePictureUrl));

In this section of code, I am creating a condition where this line will evaluate as true:

if (!String.Equals(user.ProfilePictureUrl, _defaultPic))

I would like to verify that System.IO.File.Delete is called.

What is the best way to do this?

Do I need to refactor by wrapping the System.IO.File.Delete call in my own class that implements an interface so that I can mock it and verify that it was called?

I am using Moq.

1
Your last statement is absolutely correct. Encapsulate the IO calls with an abstraction that would allow you to mock it.Nkosi
Thank you @Nkosi !Epsilon Semi-Moron
You should also do the same for Service.MapPath Those are implementation concerns that can be abstracted out. Actually that whole statement can be encapsulated in one abstraction.Nkosi

1 Answers

1
votes

Do I need to refactor by wrapping the System.IO.File.Delete call in my own class that implements an interface so that I can mock it and verify that it was called?

Yes

Encapsulate the implementation concerns

public interface IFileSystem {
    void Delete(string path);

    //...code removed for brevity
}

public class ServerFileSystemWrapper : IFileSystem {
    public void Delete(string path) {
        System.IO.File.Delete(Server.MapPath(path));
    }

    //...code removed for brevity
}

Which would be explicitly injected into dependents via constructor injection and used.

if (!String.Equals(user.ProfilePictureUrl, _defaultPic))
    _fileSystem.Delete(user.ProfilePictureUrl); //IFileSystem.Delete(string path)

This would now allow mocks to be setup and verified as needed

//Arrange
var mockFile = new Mock<IFileSystem>();

var profilePictureUrl = "...";

//...code removed for brevity

var sut = new AccountController(mockFile.Object, ....);

//Act
var result = sut.EditProfile(model);

//Assert
result.Should().NotBeNull();
mockFile.Verify(_ => _.Delete(profilePictureUrl), Times.AtLeastOnce());