하드 코딩 된 객체로 메소드를 조롱하는 방법?


11

여러 레이어가있는 응용 프로그램을 작성 중입니다. 데이터 소스에서 데이터를 검색하고 저장하는 데이터 액세스 계층, 데이터를 조작하는 비즈니스 로직, 화면에 데이터를 표시하는 사용자 인터페이스.

또한 비즈니스 로직 계층의 단위 테스트를 수행합니다. 유일한 요구 사항은 비즈니스 계층 논리의 흐름을 테스트하는 것입니다. 그래서 Moq 프레임 워크를 사용하여 데이터 액세스 계층을 조롱하고 MS Unit으로 비즈니스 로직 계층을 단위 테스트합니다.

단위 테스트를 수행 할 수 있도록 인터페이스 프로그래밍을 사용하여 설계를 최대한 분리합니다. 인터페이스를 통한 비즈니스 계층 통화 데이터 액세스 계층.

비즈니스 논리 방법 중 하나를 테스트하려고 할 때 문제가 있습니다. 이 방법은 일부 작업을 수행하고 객체를 생성하여 데이터 액세스 계층으로 전달합니다. 해당 데이터 액세스 계층 방법을 조롱하려고하면 성공적으로 조롱 할 수 없습니다.

여기에 내 문제를 보여주기 위해 데모 코드를 만들려고합니다.

모델:

public class Employee
{
    public string Name { get; set; }
}

데이터 액세스 계층 :

public interface IDal
{
    string GetMessage(Employee emp);
}

public class Dal : IDal
{
    public string GetMessage(Employee emp)
    {
        // Doing some data source access work...

        return string.Format("Hello {0}", emp.Name);
    }
}

비즈니스 로직 계층 :

public interface IBll
{
    string GetMessage();
}

public class Bll : IBll
{
    private readonly IDal _dal;

    public Bll(IDal dal)
    {
        _dal = dal;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method.
        Employee emp = new Employee(); 

        string msg = _dal.GetMessage(emp);
        return msg;
    }
}

단위 테스트 :

[TestMethod]
    public void Is_GetMessage_Return_Proper_Result()
    {
        // Arrange.
        Employee emp = new Employee; // New object.

        Mock<IDal> mockDal = new Mock<IDal>();
        mockDal.Setup(d => d.GetMessage(emp)).Returns("Hello " + emp.Name);

        IBll bll = new Bll(mockDal.Object);

        // Act.

        // This will create another employee object inside the 
        // business logic method, which is different from the 
        // object which I have sent at the time of mocking.
        string msg = bll.GetMessage(); 

        // Assert.
        Assert.AreEqual("Hello arnab", msg);
    }

조롱 할 때 단위 테스트 사례에서 Employee 객체를 보내고 있지만 비즈니스 로직 메소드를 호출하면 메소드 내부에 다른 Employee 객체가 생성됩니다. 그래서 나는 객체를 조롱 할 수 없습니다.

이 경우 문제를 해결할 수 있도록 디자인하는 방법은 무엇입니까?


일반적으로 트릭은 객체를 인터페이스로 감싸서 모든 소비자가 해당 인터페이스를 사용하게하고 인터페이스를 조롱하는 것입니다. 또는 메서드를 가상으로 만든 다음 moq가 인터페이스없이 메소드를 조롱 할 수 있습니다. 그러나이 경우 코뿔소 또는 다른 것들에 대해서는 확실하지 않습니다.
Jimmy Hoffa

답변:


12

클래스 를 사용 Employee하여 직접 객체 를 생성하는 대신 클래스 는 생성자를 통해 주입되는 메소드와 함께 클래스를 사용할 수 있습니다 .newBllEmployeeFactorycreateInstance

 class EmployeeFactory : IEmployeeFactory
 {
       public Employee createInstance(){return new Employee();}
 }

생성자는 interface를 통해 팩토리 객체를 IEmployeeFactory가져와야하므로 "실제"팩토리를 모의 팩토리로 쉽게 바꿀 수 있습니다.

public class Bll : IBll
{
    private readonly IDal _dal;
    private readonly IEmployeeFactory _employeeFactory;

    public Bll(IDal dal, IEmployeeFactory employeeFactory)
    {
        _dal = dal;
        _employeeFactory=employeeFactory;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method
        // *** using a factory ***
        Employee emp = _employeeFactory.createObject(); 
        // ...
    }
    //...
}

모의 팩토리는 테스트에 필요한 모든 종류의 Employee개체를 테스트에 제공 할 수 있습니다 (예 createInstance: 항상 같은 개체를 반환 할 수 있음).

 class MockEmployeeFactory : IEmployeeFactory
 {
       private Employee _emp;

       public MockEmployeeFactory()
       {
          _emp = new Employee();
          // add any kind of special initializing here for testing purposes
       }

       public Employee createInstance()
       {
          // just for testing, return always the same object
          return _emp;
       }
 }

이제 테스트에서이 모형을 사용하여 트릭을 수행해야합니다.


이론을 시각화 할 수 있도록 하나의 코드 예제를 제공해 주시겠습니까?
DeveloperArnab

@DeveloperArnab : 내 편집을 참조하십시오.
Doc Brown

매우 유용합니다 ...
DeveloperArnab

4

테스트 할 단일 단위로 처리합니다.

Employee객체가 생성 되는 모든 입력을 제어 하는 한 테스트 된 객체에서 생성 된 사실은 중요하지 않습니다. 인수의 내용이 예상과 일치하는 경우 예상 결과를 반환하려면 mock 메소드가 필요합니다.

분명히 그것은 당신이 mock 메소드를위한 커스텀 로직을 제공해야한다는 것을 의미합니다. 고급 로직은 종종 "for x return y"종류의 mock으로 테스트 할 수 없습니다.

사실, 당신은해야 하지 가 생산 것보다 당신이 한 경우에, 당신이 그것을 작성해야하는 코드를 테스트하지 않기 때문에 그것은, 테스트에 다른 개체를 반환합니다. 그러나이 코드는 프로덕션 코드의 필수 부분이므로 테스트 사례에서도 다루어야합니다.


예, 데이터 액세스 계층의 입력에 신경 쓰지 않고 비즈니스 논리를 테스트 할 수 있도록 해당 객체를 조롱하고 하드 코딩 된 데이터 만 반환하려고합니다. 그러나 문제는 두 개의 다른 Employee 객체로 인해 데이터 액세스 계층 방법을 조롱 할 수 없다는 것입니다.
DeveloperArnab

@DeveloperArnab : 객체는 다르지만 알려진 내용이 있습니다. 따라서 개체 ID 대신 모의 사용자 지정 비교를 수행하기 만하면됩니다.
Jan Hudec

@DeveloperArnab : Employee테스트에 다른 객체 를 주입 하면 일반적으로 생성하는 코드를 테스트하지 않습니다. 따라서 변경해서는 안됩니다.
Jan Hudec

0

테스트 도구가 실패하기 때문에 항상 인터페이스를 사용해야하며 인터페이스 기반 객체를 다른 인터페이스로 교체 할 수있는 방식으로 모든 것을 만들어야합니다.

그러나 더 나은 도구가 있습니다. Microsoft Fakes (Moles라고 함)를 사용하면 정적 개체와 전역 개체를 모두 바꿀 수 있습니다. 객체를 교체하는 데 더 낮은 수준의 접근 방식이 필요하므로 익숙한 테스트 작성 방법을 유지하면서 어디서나 인터페이스를 사용할 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.