2017-09-26 75 views
2

有DataService类像下面我如何单元测试代码依赖于一个工厂

public class LessonDataService 
{ 

    IUnitOfWork unitOfWork = UnitOfWorkFactory.CreateUnitOfWork(); 

    public Lesson FindById(int id) 
    { 
     try 
     { 
      return unitOfWork.Lessons.FindById(id); 
     } 
     catch (Exception ex) 
     { 
      throw exception; 
     } 
    } 
} 

正如你可以看到的UnitOfWork是DataService类的内部构造。我也不希望通过构造函数传递UnitOfWork,我不希望UI代码打扰UnitOfWork,我只是想调用DataService类并让它完成剩下的工作。有任何想法吗?

+1

使用静态工厂往往会使他们的依赖项孤立测试比他们需要更困难。你是正确的,因为UI不应该关注UOW。但它与数据服务有关。 – Nkosi

+0

你可以在'UnitOfWorkFactory'中注入一些依赖吗? – doctorlove

回答

0

使用Mocking框架。单元测试是为了测试这段代码而不是分支或连接类。使用Mockito或easymock等模拟框架,模拟UnitOfWorkFactory并为unitOfWork.Lessons.FindById(id)创建自己的输出。记住要测试所有的可能性。使用此link了解更多关于Mockito。

+0

我已经使用了Moq,但如果我嘲笑UnitOfWorkFactory,我如何将它注入到DataService类中,以便我使用它而不是使用真正的Factory。 – Sisyphus

5

如何依赖于一个工厂

简答单元测试代码:你不

Abstract Factories are a code smell

紧密耦合的代码是难以测试。 (但并非不可能)。

通过构造函数注入重构你的代码以显式依赖。 (避免服务定位器反模式)

public class LessonDataService : ILessonDataService {  
    private readonly IUnitOfWork unitOfWork; 

    public LessonDataService(IUnitOfWork unitOfWork) { 
     this.unitOfWork = unitOfWork; 
    } 

    public Lesson FindById(int id) { 
     try { 
      return unitOfWork.Lessons.FindById(id); 
     } catch (Exception ex) { 
      throw exception; 
     } 
    } 
} 

我不想让UI代码与的UnitOfWork打扰我只是想它来调用DataService类,让它做休息。

然后抽象服务,注入到用户界面,让它做剩下的。 UI不应该直接关注UOW。

public interface ILessonDataService { 
    Lesson FindById(int id); 
} 

通过重构依靠明确的抽象,代码是现在更加灵活,可以在隔离以最小的负面影响进行测试。

[TestMethod] 
public void DataService_Should_Get_Lesson() { 
    //Arrange 
    var id = 1; 
    var lesson = new Lesson { 
     Id = id, 
     //...code removed for brevity 
    }; 
    var mock = new Mock<IUnitOfWork>(); 
    mock.Setup(_ => _.Lessons.FindById(id)).Returns(lesson); 

    var sut = new LessonDataService(mock.Object); 

    //Act 
    var actual = sut.FindById(id); 

    //Assert 
    lession.Should().BeEquivalentTo(actual); 
} 

在生产代码中,当前工厂仍可以在组合根中注册DI容器,但仍然保持代码解耦。

例如(在.net核心)

services.AddTransient<IUnitOfWork>(_ => UnitOfWorkFactory.CreateUnitOfWork()); 
services.AddTransient<ILessonDataService, LessonDataService>(); 
+0

不幸的是我出于政治原因不能改变Factory类和导出UnitOfWork,但是非常感谢你的努力。 – Sisyphus

+0

@Sisyphus,我根本没有改变工厂类。我重构了依赖于工厂类的代码。这就是你无法更改代码的意思吗? – Nkosi

+0

对不起,我不是故意改变工厂,我的意思是将UnitOfWork暴露给外观。我们需要将它完全封装在DataService类中,并且即使抽象概念也完全无知。 – Sisyphus

0

我落户的是,我做的内部构造函数DataService和使用InternalsVisibleToAttribute,这样我可以覆盖使其单元测试项目可见工厂和注射和模拟UnitOfWork。

相关问题