简体   繁体   English

模型视图演示者测试的分辨率...我是使用DTO还是域对象,还是两者都使用?

[英]Resolution for Model View Presenter Testing… Do I use DTO's or Domain objects or both?

The basic issue is how to test a presenter. 基本问题是如何测试演示者。

Take: Domain object (will eventually be persisted to DB) Base attributes are Id (DB ID, Int/GUID/Whatever) and TransientID (Local ID until saved, GUID) Take:Domain对象(最终将持久保存到DB)Base属性是Id(DB ID,Int / GUID / Whatever)和TransientID(本地ID直到保存,GUID)

DomainObject domainObject的


namespace domain {  
  public class DomainObject {    
   private int _id;    
   private Guid transientId;    
   public DomainObject()
   {      
    _transient_Id = Guid.NewGuid();    
   }
  }
}

PresenterTest: PresenterTest:


var repository = Mock.StrictMock();
var view = Mock.StrictMock();

view.Save += null;
var saveEvent = LastCall.Ignore().GetEventRaiser();

var domainObject = new DomainObject() {Id = 0, Attribute = "Blah"};

Mock.ExpectCall(Repository.Save(domainObject)).Returns(True);
Mock.ReplayAll();

var sut = new Presenter(repository, view);
Save_Event.raise(view, EventArgs.Empty);

Mock.Verify()

So the problem here is that the domain object identity is calculated with ID and failing that it's calculated with transientID, there's no way to know what the transientID will be so I can't have the mock repository check for equality. 所以这里的问题是域对象标识是用ID计算的,并且没有用transientID计算,没有办法知道transientID是什么,所以我不能让mock存储库检查是否相等。

The workarounds so far are: 到目前为止的解决方法是:

1) LastCall.Ignore and content myself with jsut testing that the method got called but not test the content of the call. 1)LastCall.Ignore和我自己用jsut测试该方法被调用但不测试调用的内容。

2) Write a DTO to test against and save to a service. 2)编写DTO以测试并保存到服务。 The service than handles the mapping to domain. 该服务比处理到域的映射。

3) Write a fake testRepository that uses custom logic to determine success. 3)编写一个假的testRepository,它使用自定义逻辑来确定成功。

--1 doesn't test the majority of the logic. --1不测试大部分逻辑。 --2 is a lot of extra code for no good purpose --3 Seems potentially brittle. --2是一个没有好处的额外代码--3似乎可能很脆弱。

Right now I'm leaning towards DTO's and a service in the theory that it gives the greatest isolation between tiers but is probably 75% unnecessary... 现在我倾向于DTO和理论上的服务,它提供了层之间最大的隔离,但可能是75%不必要的......

WPF has helped me realize that you really don't need to do much testing if any on the Controller/Presenter/VM. WPF帮助我意识到你真的不需要在Controller / Presenter / VM上进行太多测试。 You really should focus all of your tests on the models and services you use. 您真的应该将所有测试集中在您使用的模型和服务上。 All business logic should be there, the view model or presenter or controller should be as light as possible, and only role is to transfer back and forth between the model and the view. 所有业务逻辑都应该存在,视图模型或演示者或控制器应尽可能轻,并且只有角色是在模型和视图之间来回传递。

What's the point of testing whether you call a service when a button command makes it to the presenter? 当按钮命令进入演示者时,测试是否调用服务有什么意义? Or testing whether an event is wired properly? 或者测试一个事件是否正确连接?

Don't get me wrong, I still have a very small test fixture for the view models or controllers but really the focus of tests should be on the models, let the integration tests test the success of the view and the presenter. 不要误解我的意见,我仍然有一个非常小的测试夹具用于视图模型或控制器,但实际上测试的重点应放在模型上,让集成测试测试视图和演示者的成功。

Skinny controllers/VMs/Presenters. 瘦的控制器/ VM /演示者。 Fat Models. 胖模特。

This is my answer because I ran into the same issue trying to test viewmodels, I wasted so much time trying to figure out how best to test them and another developer gave a great talk on Model-View patterns with this argument. 这是我的答案,因为我遇到了同样的问题,试图测试视图模型,我浪费了太多时间试图弄清楚如何最好地测试它们,另一个开发人员用这个参数对模型视图模式进行了很好的讨论。 Don't spend too much time making tests for these, focus on the models/services. 不要花太多时间为这些测试,专注于模型/服务。

there's no way to know what the transientID will be so I can't have the mock repository check for equality. 没有办法知道transientID是什么,所以我不能让mock存储库检查是否相等。

Actually, I think there is an opportunity here. 实际上,我认为这里有机会。

Instead of calling Guid.NewGuid() , you could create your own GuidFactory class that generates GUIDs. 您可以创建自己的GuidFactory类来生成GUID,而不是调用Guid.NewGuid() By default, it would use Guid.NewGuid() internally, but you could take control of it for tests. 默认情况下,它会在内部使用Guid.NewGuid() ,但您可以控制它进行测试。

public static class GuidFactory
{
    static Func<Guid> _strategy = () => Guid.NewGuid();

    public static Guid Build()
    {
        return _strategy();
    }

    public static void SetStrategy(Func<Guid> strategy)
    {
        _strategy = strategy;
    }
}

In your constructor, you replace Guid.NewGuid() with GuidFactory.Build() . 在构造函数中,用Guid.NewGuid()替换GuidFactory.Build()

In your test setup, you override the strategy to suit your needs -- return a known Guid that you can use elsewhere in your tests or just output the default result to a field. 在您的测试设置中,您可以覆盖策略以满足您的需求 - 返回您可以在测试中的其他位置使用的已知Guid ,或者仅将默认结果输出到字段。

For example: 例如:

public class PseudoTest
{
    IList<Guid> GeneratedGuids = new List<Guid>();

    public void SetUpTest()
    {
        GuidFactory.SetStrategy(() => 
        {
            var result = Guid.NewGuid();
            GeneratedGuids.Add(result);
            return result;
        });
    }

    public void Test()
    {
        systemUnderTest.DoSomething();
        Assert.AreEqual(GeneratedGuids.Last(), someOtherGuid);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM