출처:

http://blog.naver.com/empty_wagon/20151928869

http://blog.naver.com/empty_wagon/20151986599

 

 

사용하는가?

  • Mock 라이브러리는 단위 테스트를 쉽게 하기 위해 존재합니다.
  • 예를 들어서, 클래스 테스트에 데이터베이스를 활용해야 하는 경우가 있다고 가정을 합시다. 경우 직접 데이터베이스에 정보를 넣었다 뺐다 하면 상당히 부담이 됩니다.
  • 이런 부담을 주지 않고, 데이터베이스의 행동을 흉내냄으로써 테스트를 더욱 용이하게 합니다.
  • 하지만 이런 용이함을 얻기 위해서는 인터페이스들에 의해서만 프로그램을 조작하는 설계를 해야 하기는 합니다. (이런 설계가 처음에는 많이 어색하지만, 나중에는 이런 설계가 나은 설계임을 깨닫게 됩니다.)
  • 간단히 이야기해서 Mock 라이브러리는 복잡하고 시간이 걸리는 테스트의 시간을 줄여주기 위해서 존재한다고 있습니다.

 

설치

 

예제

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

}

   

public class PriceProcessor

{

    public PriceProcessor(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public int GetTotalPrice()

    {

        int result = 0;

               

        foreach (Book b in bookRepo.GetBooks())

        {

            result += b.Price;

        }

    

        return result;

    }

    

    private IBookRepository bookRepo;

}

   

   

[TestFixture]

public class PriceProcessorTests

{

    [Test]

    public void GetTotalPrice_NoDiscount_ReturnCorrectPriceSum()

    {

        List<Book> bookList = new List<Book>()

        {

            new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

            new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

            new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

        };

    

        Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

        bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

        PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

        Assert.That(priceProcessor.GetTotalPrice(), Is.EqualTo(60000));

    }

}

  • bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

  • GetBooks함수가 불리면, bookList 반환하라고 명령을 내리고 있습니다. 그리고 완성이 bookRepoMock Object 속성을 이용해서 priceProcessor 넘겨 주고 있습니다

 

함수가 불려졌는지 확인

 

 

public class BestSellers

{

    public BestSellers(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public Result AddBook(Book book)

    {

        if (book.Name.Contains("<") || book.Name.Contains(">"))

        {

            return Result.XssAlert;

        }

               

        bookRepo.AddBook(book);

        return Result.Success;

    }

    

    IBookRepository bookRepo;

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

    bool AddBook(Book book);

}

   

   

[Test]

public void AddBook_ContainsTag_IBookRepositoryAddBookIsNotCalled()

{

    Book b = new Book() { Name="<iframe></iframe>"};

    

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Verifiable();

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    bestSellers.AddBook(b);

    

    mock.Verify(r => r.AddBook(b), Times.Never());

}

 

Mock 객체에서 생성된 함수들을 매개 변수에 따라 동작을 조절

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public void ReducePrice(float reductionRatio)

{

    foreach (Book b in bookRepo.GetBooks())

    {

        b.Price = (int)(reductionRatio * b.Price);

        bookRepo.UpdateBook(b);

    }

}

   

   

[Test]

public void ReducePrice_ValueBetween0And1_IBookRepositoryUpdateBookCalled()

{

    List<Book> bookList = new List<Book>()

    {

        new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

        new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

        new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

    };

    

    Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

    bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

    PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

    priceProcessor.ReducePrice(0.1f);

    

    bookRepoMock.Verify(r => r.UpdateBook(It.IsAny<Book>()),

Times.Exactly(bookList.Count));

}

 

Returs, Callback, Throws

 

public Result AddBook(Book book)

{

    if (book.Name.Contains("<") || book.Name.Contains(">"))

    {

        return Result.XssAlert;

    }

               

    bool added = bookRepo.AddBook(book);

    if (added == false)

    {

        return Result.AlreadyExist;

    }

    return Result.Success;

}

   

   

[Test]

public void AddBook_AddSameBookAgain_ReturnsAlreadyExist()

{

    Book b = new Book() { ISBN = "1234", Name = "abc" };

    

    int count = 0;

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Callback(() => count++).Returns(() => count <= 1);

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    Result result1 = bestSellers.AddBook(b);

    Result result2 = bestSellers.AddBook(b);

    

    Assert.That(result1, Is.EqualTo(Result.Success));

    Assert.That(result2, Is.EqualTo(Result.AlreadyExist));

}

 

참고

  • Moq活用して.NETでモックを使ったテストを

 

 

 

 

신고
by 흥배 2014.01.08 08:00
| 1 |

티스토리 툴바