我正在尝试为调用异步存储库的类创建单元测试.我正在使用ASP.NET Core和Entity Framework Core.我的通用存储库看起来像这样.
public class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : class
{
private readonly SaasDispatcherDbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public EntityRepository(SaasDispatcherDbContext dbContext)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<TEntity>();
}
public virtual IQueryable<TEntity> GetAll()
{
return _dbSet;
}
public virtual async Task<TEntity> FindByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public virtual void Add(TEntity entity)
{
_dbSet.Add(entity);
}
public virtual void Delete(TEntity entity)
{
_dbSet.Remove(entity);
}
public virtual …Run Code Online (Sandbox Code Playgroud) 我对单元测试和嘲笑很新!我正在尝试编写一些单元测试,其中包含一些与数据存储交互的代码.数据访问由IRepository封装:
interface IRepository<T> {
....
IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
....
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用具体的IoC实现的IRepository来测试的代码如下所示:
public class SignupLogic {
private Repository<Company> repo = new Repository<Company>();
public void AddNewCompany(Company toAdd) {
Company existingCompany = this.repo.FindBy(c => c.Name == toAdd.Name).FirstOrDefault();
if(existingCompany != null) {
throw new ArgumentException("Company already exists");
}
repo.Add(Company);
repo.Save();
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我正在测试SignupLogic.AddNewCompany()本身的逻辑,而不是逻辑和具体的存储库,我正在模拟IRepository并将其传递给SignupLogic.模拟的存储库看起来像这样:
Mock<Repository> repoMock = new Mock<Repository>();
repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc")....
Run Code Online (Sandbox Code Playgroud)
它返回一个内存中的IEnumberable,其中包含名称设置为"Company Inc"的Company对象.调用SignupLogic.AddNewCompany的单元测试会设置一个具有重复详细信息和trys的公司来传递它,并且我断言抛出了ArgumentException,并显示消息"公司已存在".这个测试没有通过.
在运行时通过单元测试和AddNewCompany()进行调试,似乎existingCompany始终为null.无奈之下,我发现如果我更新SignupLogic.AddNewCompany()以便对FindBy的调用如下所示:
Company existingCompany = this.repo.FindBy(c => c.Name == "Company Inc").FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)
测试通过,这告诉我,Moq只响应与我在测试夹具中设置的代码完全相同的代码.显然,在测试任何重复的公司被SignupLogic.AddNewCompany拒绝时,这并不是特别有用.
我已经尝试设置moq.FindBy(...)来使用"Is.ItAny",但这并不会导致测试通过.
从我正在阅读的所有内容来看,似乎我正在尝试测试表达式实际上并不能在这里使用Moq.可能吗?请帮忙!
假设我想使用Moq在setter上创建一个回调,以便将set属性存储在我自己的字段中供以后使用.(有争议的例子 - 但问题就到了.)我可以这样做:
myMock.SetupSet(x => x.MyProperty).Callback(value => myLocalVariable = value);
Run Code Online (Sandbox Code Playgroud)
这很好用.但是,根据Intellisense,SetupSet已经过时了.但它没有说应该用什么作为替代品.我知道moq提供了SetupProperty,它将使用支持字段自动装配该属性.但这不是我想要的.我想将设定值捕获到我自己的变量中.我应该如何使用非过时的方法?
设置如下:
public interface IFoo
{
void Fizz();
}
[Test]
public void A()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz());
// stuff here
foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}
Run Code Online (Sandbox Code Playgroud)
基本上我想// stuff here在foo.Verify(x => x.Fizz(), Times.Never())传递中输入一些代码.
因为这可能构成moq/unit测试滥用,我的理由是我可以这样做:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// reset the verification …Run Code Online (Sandbox Code Playgroud) 我一直在使用RhinoMocks,但刚开始研究Moq.我有这个非常基本的问题,令我感到惊讶的是,这并不是开箱即用的.假设我有以下类定义:
public class Foo
{
private IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}
..
}
Run Code Online (Sandbox Code Playgroud)
现在我有一个测试,我需要模拟发送到Foo的IBar.在RhinoMocks中,我会像下面这样做,它会很有效:
var mock = MockRepository.GenerateMock<IBar>();
var foo = new Foo(mock);
Run Code Online (Sandbox Code Playgroud)
但是,在Moq中,这似乎并没有以同样的方式起作用.我这样做:
var mock = new Mock<IBar>();
var foo = new Foo(mock);
Run Code Online (Sandbox Code Playgroud)
然而,现在它失败了 - 告诉我"无法从'Moq.Mock'转换为'IBar'.我做错了什么?用Moq做这个的推荐方法是什么?
我需要测试以下方法:
CreateOutput(IWriter writer)
{
writer.Write(type);
writer.Write(id);
writer.Write(sender);
// many more Write()s...
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个Moq'd IWriter,我想确保Write()以正确的顺序调用这些方法.
我有以下测试代码:
var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
var sequence = new MockSequence();
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender));
Run Code Online (Sandbox Code Playgroud)
但是,第二次调用Write()in CreateOutput()(写入id值)会抛出一条MockException消息" IWriter.Write()调用失败,模拟行为为Strict.模拟上的所有调用都必须有相应的设置. "
我也发现很难找到任何明确的,最新的Moq序列文档/示例.
我做错了什么,或者我不能使用相同的方法设置序列?如果没有,有没有我可以使用的替代品(最好使用Moq/NUnit)?
我在哪里可以找到MOQ的综合文档?我只是从嘲笑开始,我很难理解它.我已经阅读了http://code.google.com/p/moq/wiki/QuickStart上的所有链接,但似乎无法找到教程或温和的介绍.
我还简要介绍了Rhino Mocks,但发现它非常令人困惑.
是的 - 我读过Stephen Walthers的文章 - 非常有帮助.我也通过了链接.我似乎无法在http://www.bestechvideos.com/2008/06/08/dimecasts-net-introduction-to-mocking-with-moq 观看视频[断链]
具体来说,我试图确定是否从模拟类中引发了一个事件.我无法获得QuickStarts页面上的事件编译示例.在google组中,Daniel解释说CreateEventHandler只能处理类型的事件EventHandler<TEventArgs>,但即使这样我也无法获得编译代码.
更具体地说,我有一个实现的类INotifyChanged.
public class Entity : INotifyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public int Id
{
get {return _id;}
set {
_id = value;
OnPropertyChanged("Id");
}
}
protected void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
etc .....
}
Run Code Online (Sandbox Code Playgroud)
如何模拟该类以测试PropertyChanged事件是否被触发?我不能重写事件,public event EventHandler<PropertyChangedEventArgs>因为我得到这个错误:
错误1'CoreServices.Notifier'未实现接口成员System.ComponentModel.INotifyPropertyChanged.PropertyChanged'.'CoreServices.Notifier.PropertyChanged'无法实现'System.ComponentModel.INotifyPropertyChanged.PropertyChanged',因为它没有匹配的返回类型'System.ComponentModel.PropertyChangedEventHandler'.
我有一个Transfer类,简化它看起来像这样:
public class Transfer
{
public virtual IFileConnection source { get; set; }
public virtual IFileConnection destination { get; set; }
public virtual void GetFile(IFileConnection connection,
string remoteFilename, string localFilename)
{
connection.Get(remoteFilename, localFilename);
}
public virtual void PutFile(IFileConnection connection,
string localFilename, string remoteFilename)
{
connection.Get(remoteFilename, localFilename);
}
public virtual void TransferFiles(string sourceName, string destName)
{
source = internalConfig.GetFileConnection("source");
destination = internalConfig.GetFileConnection("destination");
var tempName = Path.GetTempFileName();
GetFile(source, sourceName, tempName);
PutFile(destination, tempName, destName);
}
}
Run Code Online (Sandbox Code Playgroud)
IFileConnection界面的简化版本如下所示:
public interface IFileConnection …Run Code Online (Sandbox Code Playgroud) 我想模拟第一次返回0,然后在调用方法的任何时候返回1.问题是,如果方法被调用4次,我应该写:
mock.SetupSequence(x => x.GetNumber())
.Returns(0)
.Returns(1)
.Returns(1)
.Returns(1);
Run Code Online (Sandbox Code Playgroud)
否则该方法返回null.
有没有办法写下次在第一次调用该方法后,该方法返回1?谢谢
为SetupSequence设置更多"运营商"是否合适?如果您认为是,您可以投票:http: //moq.uservoice.com/forums/11304-general/suggestions/2973521-setupsequence-more-operators
moq ×10
c# ×9
unit-testing ×7
mocking ×5
.net ×3
asp.net-core ×1
nunit ×1
sequential ×1
testing ×1