2014-07-06 23 views
0

我为我的单元测试使用了Moq框架,最近我写了一个测试来检查我的DTO中的某个属性是否在方法中设置。什么时候在DTO模型上使用接口?

原来,DTO是由我的方法返回....然后属性设置。 以下是我正在测试的方法。

public void UpdatePlayerProfileStatistics(DataRow playerRecord, bool useUpdatedGamesPlayed = true) 
    { 
     DTOProfile.Player player = this._playerDataParser.ParsePlayerProfileData(playerRecord); 

     DTOProfile.Player playerInDatabase = this._playerManager.MatchPlayerInDatabase(player.Id); 

     string errorMessage = String.Empty; 
     if (useUpdatedGamesPlayed) 
     { 
      // do nothing. player.Games = player.Games; nothing changes 
     } 
     else 
     { 
      // we want to keep the Games played from the value of the player in the database. 
      player.Games = playerInDatabase.Games; 
     } 

     if (!this.repository.UpdatePlayerProfile(player, out errorMessage)) 
     { 
      throw new InvalidOperationException(errorMessage); 
     } 
    } 

对于我的测试,我会嘲笑ParsePlayerProfileData和MatchPlayerInDatabase,但测试.Games属性的设置,我需要一个IP层接口添加到播放器。 然后,我会让ParsePlayerProfileData和MatchPlayerInDatabase返回接口。

DTOProfile.IPlayer player = this._playerDataParser.ParsePlayerProfileData(playerRecord); 

DTOProfile.IPlayer playerInDatabase = this._playerManager.MatchPlayerInDatabase(player.Id); 

然后在我的测试中,我有: -

var mockUpdatedPlayer = new Mock<IPlayer>(); 
var mockDatabasePlayer = new Mock<IPlayer>(); 
_playerDataParser.Setup(p => p.ParsePlayerProfileData(It.IsAny<DataRow>())).Returns(mockUpdatedPlayer.Object); 
_playerManager.Setup(m => m.MatchPlayerInDatabase(It.IsAny<int>())).Returns(mockDatabasePlayer.Object); 

UpdatePlayerProfileStatistics(myRow, true); 

mockedPlayer.VerifySet(x => x.Games = It.IsAny<int>(), Times.Never()); 

这一切工作。

我的问题是,我应该为所有DTO提供接口,并让所有方法返回这些接口(而不是具体类型)? 我想这将允许我执行这些'VerifySet'和'VerifyGet's。

或者,有没有更好的方法来测试呢?

回答

2

您不想嘲笑DTO,正确构建的DTO是您测试的结果,因此您希望对其进行断言。

该方法应返回有问题的DTO实例,然后您可以验证该实例。没有必要的接口。

如果你模拟了交互的每个方面,你所测试的只是模拟框架的工作原理。

我的直觉是,在这里你的方法做太多...对重构一个建议:鉴于

  • 一个DataRow,玩家:

    public DTOProfile.Player UpdatePlayerProfileStatistics(DataRow playerRecord, bool useUpdatedGamesPlayed = true) 
    { 
        DTOProfile.Player player = this._playerDataParser.ParsePlayerProfileData(playerRecord); 
    
        DTOProfile.Player playerInDatabase = this._playerManager.MatchPlayerInDatabase(player.Id); 
    
        string errorMessage = String.Empty; 
        if (useUpdatedGamesPlayed) 
        { 
         // do nothing. player.Games = player.Games; nothing changes 
        } 
        else 
        { 
         // we want to keep the Games played from the value of the player in the database. 
         player.Games = playerInDatabase.Games; 
        } 
    
        return player; 
    } 
    
    public void PersistPlayer(DTOProfile.Player player) 
    { 
        if (!this.repository.UpdatePlayerProfile(player, out errorMessage)) 
        { 
         throw new InvalidOperationException(errorMessage); 
        } 
    } 
    

    然后,您可以测试两种方法对象正确更新。

  • 给定一个Player例如,如果持续存在失败抛出一个异常,在不同情况下会生成正确的错误信息等
1

我认为与仅用于测试使用的代码修改你的类是一个设计的气味。 它会混乱你的DTO,并导致代码重复。此外,所有接口 将基本上重复在DTO中找到的所有属性,并且添加/删除/修改属性 您现在需要更改两个位置而不是一个位置。

你可以重写你的单元测试,没有嘲笑的DTO:

var mockUpdatedPlayer = new DTOProfile.Player(); 
var mockDatabasePlayer = new DTOProfile.Player(); 
_playerDataParser.Setup(p => p.ParsePlayerProfileData(It.IsAny<DataRow>())).Returns(mockUpdatedPlayer); 
_playerManager.Setup(m => m.MatchPlayerInDatabase(It.IsAny<int>())).Returns(mockDatabasePlayer); 
UpdatePlayerProfileStatistics(myRow, true); 
Assert.AreNotEqual(mockUpdatedPlayer.Games, mockDatabasePlayer.Games); 

或者,如果没有空播放器的构造函数,或建设是测试太重, 你可以标记你的DTO的属性,虚拟的 - 起订量可以模拟虚拟属性。

一些更多的链接支持我的观点:

http://rrees.me/2009/01/31/programming-to-interfaces-anti-pattern/

http://marekdec.wordpress.com/2011/12/06/explicit-interface-per-class-antipattern/

换一个完全相反的观点:

Are single implementer interfaces for unit testing an antipattern?

+0

呀,设计气味正是我所指的是。我喜欢使用VerifySet和VerifyGet方法的想法....但是为了使用它们而必须转换大量代码库才能使我不满意。 我喜欢虚拟属性的想法。我会试试看看是否可以缓解我的一些担忧。 –

相关问题