我想对我的函数
GetDogName
进行单元测试,这将创建一个新的DbContext
。我已经有了我的 Mock
的工作 DbContext
,但我无法将它作为参数传递到任何地方。
我可以用函数中的其他内容替换
DbContext context = new()
。但我无法将 DbContext
作为参数传递。
我也检查过工厂是否可行(或其他什么?),但尚未找到解决方案。
static class AnimalsHelper
{
public static string GetDogName() // I can not pass parameters here
{
// I can change this line
using (DbContext context = new())
{
return context.Dogs.First().Name;
}
}
}
class UnitTest
{
[SetUp]
public void SetUp()
{
ContextMock = new Mock<DbContext>();
Context = ContextMock.Object;
ContextMock.Setup(context => context.Dogs).Returns("Rex");
}
[Test]
public void GetDogName_DogName_ReturnsRex()
{
var expected = "Rex";
var actual = AnimalsHelper.GetDogName();
Assert.AreEqual(expected, actual);
}
}
public class AnimalDbContext : DbContext
{
// !!! Must be virtual
public virtual DbSet<Dog> Dogs { get; set; }
}
public class AnimalService
{
private AnimalDbContext _context;
public AnimalService(AnimalDbContext context)
{
_context = context;
}
public string GetDogName()
{
return _context.Dogs.FirstOrDefault()?.Name;
}
}
[TestClass()]
public class AnimalServiceTests
{
[TestMethod()]
public void GetDogName_DogName_ReturnsRex()
{
var data = new List<Dog>
{
new Dog { Name = "Rex" }
}.AsQueryable();
var mockSet = new Mock<DbSet<Dog>>();
mockSet.As<IQueryable<Dog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Dog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Dog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Dog>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());
var mockContext = new Mock<AnimalDbContext>();
mockContext.Setup(c => c.Dogs).Returns(mockSet.Object);
var service = new AnimalService(mockContext.Object);
// Arrange
var dogName = service.GetDogName();
var actual = "Rex";
Assert.AreEqual(actual, dogName);
}
}
我们已经通过
DbContextCreator
类解决了这个问题。
在
IDbContextCreator
中创建 Main
类的实例,然后保持静态。DbContext
不能作为参数传递到的函数使用此 static
创建者实例来创建新的 DbContext
。在单元测试中,使用了一个创建者类,它返回一个
DbContextMock
主要
internal static class Program
{
/// <summary>
/// Main application entry point
/// </summary>
internal static async Task Main(string[] args)
{
DbContextCreator.Instance = new MyDbContextCreator();
// Start Application
}
/// <inheritdoc />
private class MyDbContextCreator : IDbContextCreator
{
/// <inheritdoc />
public BaseDbContext Create() => new();
}
}
功能
public static void TestableFunction()
{
using (BaseDbContext context = DbContextCreator.Create())
{
// DB stuff
context.SaveChanges();
}
}
抽象单元测试
public abstract class AbstractTestWithMockData : AbstractTest
{
protected BaseDbContext Context { get; set; }
protected Mock<BaseDbContext> ContextMock { get; set; }
/// <inheritdoc />
protected AbstractTestWithMockData()
{
DbContextCreator.Instance = new TestDbContextCreator(this);
}
/// <inheritdoc />
[SetUp]
public override void SetUp()
{
base.SetUp();
ContextMock = new Mock<BaseDbContext>();
Context = ContextMock.Object;
// ContextMock.Setup()
}
/// <inheritdoc />
private class TestDbContextCreator : IDbContextCreator
{
private readonly AbstractTestWithMockData AbstractTest;
public TestDbContextCreator(AbstractTestWithMockData abstractTest)
{
AbstractTest = abstractTest;
}
/// <inheritdoc />
public BaseDbContext Create()
{
return AbstractTest.Context;
}
}
}
创作者
Interface
static
Instance
/// <summary>
/// Interface for a project-specific implementation of a <see cref="BaseDbContext" /> creator.<br />
/// The creator is required in order to be able to unit test functions
/// where a <see cref="BaseDbContext" /> cannot be passed as a parameter.
/// </summary>
public interface IDbContextCreator
{
BaseDbContext Create();
}
/// <summary>
/// Static database creator that holds an instance of <see cref="IDbContextCreator" />
/// to be able to create an instance of <see cref="BaseDbContext" />.
/// </summary>
public static class DbContextCreator
{
/// <summary>
/// Static instance of <see cref="IDbContextCreator" /> for the <see cref="Create" /> function.
/// </summary>
[SuppressMessage("Design", "CA1044:Properties should not be write only")]
public static IDbContextCreator? Instance { private get; set; }
/// <summary>
/// Creates a new instance of <see cref="BaseDbContext" />.
/// </summary>
/// <returns>A new instance of <see cref="BaseDbContext" /></returns>
/// <exception cref="Exception">When the <see cref="Instance" /> is not set</exception>
public static BaseDbContext Create()
{
if (Instance is null)
{
throw new Exception("Database creator instance is not set! Covfefe!");
}
return Instance.Create();
}
}