1

これは私のドメインクラスです

public partial class Department
{
    public int DepartmentId { get; set; }
    [Required]
    public string DepartmentCode { get; set; }
    [Required]
    public string DepartmentFullName { get; set; }
    public bool Status { get; set; }
    public System.DateTime CreatedOn { get; set; }
}

私の MVC アプリケーションでは、これが私のDepartmentServiceクラスの外観です。

public class DepartmentService : IDepartmentService
{
    private IUnitOfWork _UoW;
    private IRepository<Department> repository;
    public DepartmentService(IUnitOfWork UoW)
    {
        _UoW = UoW;
        repository = _UoW.GetRepository<Department>();
    }

    public IList<Department> GetAllDepartments()
    {
        return repository.GetAll();
    }

    public bool SaveDepartment(Department newDepartment)
    {
        try
        {
            repository.Add(newDepartment);
            _UoW.Save();
        }
        catch (Exception)
        {
            throw;
        }
        return true;
    }
}

以下のように、GetAllDepartments メソッドの単体テストを 1 つ作成しました。

[Test]
public void When_GetAllDepartments_Is_Called_RepositoryGetAll_ShouldBeCalled()
{
    // Arrange
    var mockUnitOfWork = new Mock<IUnitOfWork>();
    var mockRepository = new Mock<IRepository<Department>>();
    mockUnitOfWork.Setup(x => x.GetRepository<Department>())
                  .Returns(mockRepository.Object);
    var sut = new DepartmentService(mockUnitOfWork.Object);

    // Act
    sut.GetAllDepartments();

    // Assert
    mockRepository.Verify(x => x.GetAll());
}

SaveDepartment部門が正常に保存されたときに true を返すメソッドをテストしたいと思います。このための単体テストを書くことができません。

また、DepartmentCode または DepartmentFullName が空白で、保存が試行された場合に例外がスローされることもテストしたいと考えています。

これは私がこれまでに持っているものです。

[Test]
public void ShouldSucessfully_SaveNewDepartment()
{
    // Arrange
    var mockUnitOfWork = new Mock<IUnitOfWork>();
    var mockRepository = new Mock<IRepository<Department>>();
    Department newDept = new Department { 
          CreatedOn = DateTime.Now,
          Status = true, 
          DepartmentFullName = "DFN", 
          DepartmentCode = "DC" };
    mockUnitOfWork.Setup(x => x.GetRepository<Department>())
                  .Returns(mockRepository.Object);
    var sut = new DepartmentService(mockUnitOfWork.Object);

    // Act
    sut.SaveDepartment(newDept);

    // Assert 
    // ???
}
4

2 に答える 2

0

まず、すべての一般的なアレンジ コードを SetUp メソッドに移動します。

private Mock<IUnitOfWork> mockUnitOfWork;
private Mock<IRepository<Department>> mockRepository;
private DepartmentService sut;

[SetUp]
public void SetUp()
{
    mockUnitOfWork = new Mock<IUnitOfWork>();
    mockRepository = new Mock<IRepository<Department>>();
    mockUnitOfWork.Setup(x => x.GetRepository<Department>())
                  .Returns(mockRepository.Object);
    sut = new DepartmentService(mockUnitOfWork.Object);
}

// tests will be here

これにより、テストの読み取りと保守がはるかに簡単になります。次へ - テストに名前を付けるときは、実装に固執しないでください。

When_GetAllDepartments_Is_Called_RepositoryGetAll_ShouldBeCalled

リポジトリ メソッドの名前を FindAll に変更するとどうなりますか? サービス メソッドの名前が変更されるとどうなりますか? テストは廃止されます。そして、誰もそれについて知りません。HOW ではなく、SUT が何をすべきかを説明する必要があります。

[Test]
public void ShouldGetAllDepartments()
{
    var expected = new List<Department>{ CreateDepartment(), CreateDepartment()};
    mockRepository.Setup(r => r.GetAll()).Returns(expected);
    var actual = sut.GetAllDepartments();
    Assert.That(actual, Is.EqualTo(expected));
    mockRepository.VerifyAll();
}

ご覧のとおり、テスト名が変更されました。また、ここでさまざまなことを確認します-リポジトリが呼び出されただけでなく(そのチェックは実際に削除できます)、そのサービスはリポジトリから取得したものとまったく同じ部門を返します。それがサービスです。そして2番目のテスト:

[Test]
public void ShouldSucessfullySaveNewDepartment()
{
    var department = CreateDepartment();
    mockRepository.Setup(r => r.Add(department));
    var result = sut.SaveDepartment(department);
    Assert.True(result);
    mockRepository.VerifyAll();
    mockUnitOfWork.Verify(u => u.Save());
}

次のサービス動作を検証します。サービスは、サービスに渡されたものとまったく同じ部門インスタンスをリポジトリに渡す必要があり、true を返す必要があります。また、作業単位で保存を呼び出してデータを送信します。

ところで、上記のように、ヘルパー メソッドを使用してテストをよりクリーンにします。

private Department CreateDepartment()
{
    return new Department { 
        CreatedOn = DateTime.Now, 
        Status = true, 
        DepartmentFullName = "DFN", 
        DepartmentCode = "DC" 
    };
}

そしてボーナス - サービスがすでに存在する部門を保存しないことを確認する:

[Test]
public void ShouldNotSaveExistingDepartment()
{
    mockUnitOfWork.Setup(u => u.Save()).Throws<NonUniqueEntityException>();
    var result = sut.SaveDepartment(CreateDepartment());
    Assert.False(result);
    mockUnitOfWork.VerifyAll();
}

ご覧のとおり、期待される動作は単純です。部門の保存中に作業単位が NonUniqueEntityException をスローした場合、サービスは false を返す必要があります。はい、false を返す方がよいと思います。このテストに合格するサービス コードは次のとおりです。

public bool SaveDepartment(Department department)
{
    try
    {
        repository.Add(department);
        _UoW.Save();
        return true;
    }
    catch (NonUniqueEntityException e)
    {
        // log exception
        return false;
    }            
}
于 2013-09-25T09:05:31.383 に答える
0

まず、冗長な try...catch を削除します。再スローするためだけに例外をキャッチしても意味がありません。ここで要求したテストに関する限り、スニペットは次のとおりです。

    [Test]
    public void ShouldSucessfully_SaveNewDepartment()
    {
        // Arrange
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        var mockRepository = new Mock<IRepository<Department>>();
        Department newDept = new Department { CreatedOn = DateTime.Now, Status = true, DepartmentFullName = "DFN", DepartmentCode = "DC" };
        mockUnitOfWork.Setup(x => x.GetRepository<Department>()).Returns(mockRepository.Object);

        var sut = new DepartmentService(mockUnitOfWork.Object);

        // Act
        bool result = sut.SaveDepartment(newDept);

        // Assert 
        Assert.That(result, Is.True);
    }

   [Test]
    public void ShouldThrowExceptionWhenExceptionThrownInternally_SaveNewDepartment()
    {
        // Arrange
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        var mockRepository = new Mock<IRepository<Department>>();
        mockUnitOfWork.Setup(x => x.GetRepository<Department>()).Returns(mockRepository.Object);
        mockUnitOfWork.Setup(uow => uow.Save()).Throws<Exception>();

        var sut = new DepartmentService(mockUnitOfWork.Object);

        // Act
        TestDelegate action = () => sut.SaveDepartment(new Department());

        // Assert 
        Assert.Throws<Exception>(action);
    }

また、DepartmentCode または DepartmentFullName が空白で、保存が試行された場合に例外がスローされることもテストしたいと考えています。

これらの値を誰がチェックするかによって異なります。これらのフィールドをチェックするためにデータベースまたは作業単位に依存したい場合、これは特定の障害条件をテストするためのフィクスチャではなく、単体テストが必要です。ここでは、uow が例外をスローした場合にのみ何が起こるかをテストする必要があります。

これらのチェックを部門サービスに追加する予定がある場合は、次のことができます。

[Test]
    public void ShouldThrowExceptionWhenDepartmentCodeIsNull_SaveNewDepartment()
    {
        // Arrange
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        Department newDept = new Department
                                 {
                                     CreatedOn = DateTime.Now, 
                                     Status = true, DepartmentFullName = "DFN", 
                                     DepartmentCode = null
                                 };

        var sut = new DepartmentService(mockUnitOfWork.Object);

        // Act
        TestDelegate action = () => sut.SaveDepartment(newDept);

        // Assert 
        Assert.Throws<ArgumentException>(action);
    }

メソッドを次のように変更した場合:

public bool SaveDepartment(Department newDepartment)
    {
        if (string.IsNullOrEmpty(newDepartment.DepartmentCode))
        {
            throw new ArgumentException("DepartmentCode must be not null");
        }

        repository.Add(newDepartment);
        _UoW.Save();
        return true;
    }
于 2013-09-25T08:34:51.457 に答える