AutoFixture 相关对象属性
AutoFixture Relating Object Properties
我正在编写一些单元测试,并且遇到了一段感觉像代码味道的代码。
基本上我有一个 Web Api。它接收传入请求 'SomeRequest':
public class SomeRequest
{
public string Name { get; set; }
public IEnumerable<int> RequestedDataIds { get; set; }
}
作为我测试的一部分。我需要确保所有这些 'RequestedDataIds' 都有效。我有一个方法调用系统的另一部分 returns 有效的 'SomeData' 对象的集合。 'SomeData' 对象包含要验证的 Id:
public class SomeData
{
public int Id { get; set; } //Id to validate against
public string Name { get; set; }
}
我的测试系统看起来像这样:
public void Execute(RequestA request)
{
var validDataIds = _someService.GetValidEntities().Select(x => x.Id).ToList();
var invalidRequestIds = request.RequestedDataIds.Where(requestedId => !validDataIds.Contains(requestedId));
if (invalidRequestIds.Any())
{
throw new SomeException("Error");
}
//Carry on as normal
}
我的问题是。是否有最佳实践或任何方式让 AutoFixture 让这两个集合相互交互,以便我可以进行测试:
- 测试当请求id无效时抛出异常
- 让所有其他测试确保它们相同,这样就不会抛出异常
- 在这种情况下甚至应该使用 AutoFixture 吗?我可以提取逻辑并使其成为 class 的新依赖项。必须然后只测试它被称为。但是,测试依赖项 class 本身仍然会引发问题 1 和 2。
这是我目前尝试解决的问题。我就是不喜欢。
1 - 确保不同的 id 抛出异常
[Test]
public void Test1()
{
_fixture = new Fixture();
var request = _fixture.Create<SomeRequest>();
var dataEnitities = _fixture.CreateMany<SomeData>();
var maxDataEntityId = dataEnitities.Select(x => x.Id).Max();
request.RequestedDataIds = new List<int> {maxDataEntityId + 1};
_someService.GetValidEntities.Returns(dataEnitities);
//Ensure exception is thrown
}
2 - 确保 ID 相同以防止异常
[Test]
public void Test1()
{
_fixture = new Fixture();
var request = _fixture.Create<SomeRequest>();
var dataEnitities = _fixture.CreateMany<SomeData>();
request.RequestedDataIds = dataEnitities.Select(x => x.Id);
_someService.GetValidEntities.Returns(dataEnitities);
//Ensure no exception is thrown
}
注意 - 此代码是实际实现的巨大缩减。因此,不必担心它 looks/is 是如何实现的。
是的,这是一种代码味道。您的测试是有效的,但它们可能很简单。
AutoFixture 是很棒的工具,但我认为在这种特定情况下您不需要它。
首先,尽量保持 Execute() 方法简单。它或多或少在 Presentation/Controller/Api 层中使用,因此尽可能保持精简。不想要任何胖控制器。
您真的只需要调用服务方法即可。
public void Execute(RequestA request)
{
_someService.ValidateEntities(request);
}
注意方法名称。它没有 return 任何东西。
您的服务可以是这样的:
public interface ISomeService
{
void ValidateEntities(RequestA request);
}
此实现将 return 来自底层数据源或服务的 ValidDataEntities。
var validDataIds = GetValidEntities().Select(x => x.Id);
我假设您从某种存储库或其他服务中获得了实体列表。
将您的逻辑推入更多 domain/poco 个对象。
创建一个简单的 POCO class 即 EntityValidator,它接受您的请求和 validDataId。
您可以只对这个 POCO class 进行单元测试,而无需使用 AutoFixture 或任何模拟。
public class EntityValidator
{
public void Validate(IEnumerable<int> validEntityIds, RequestA request)
{
var invalidRequestIds = request.RequestedDataIds.Where(requestedId => !validEntityIds.Contains(requestedId));
if (invalidRequestIds.Any())
{
throw new SomeException("Error");
}
}
}
您的单元测试将很简单,您可以使用测试框架方法,即 NUnit 中的 .Thorws 测试异常场景。
我正在编写一些单元测试,并且遇到了一段感觉像代码味道的代码。
基本上我有一个 Web Api。它接收传入请求 'SomeRequest':
public class SomeRequest
{
public string Name { get; set; }
public IEnumerable<int> RequestedDataIds { get; set; }
}
作为我测试的一部分。我需要确保所有这些 'RequestedDataIds' 都有效。我有一个方法调用系统的另一部分 returns 有效的 'SomeData' 对象的集合。 'SomeData' 对象包含要验证的 Id:
public class SomeData
{
public int Id { get; set; } //Id to validate against
public string Name { get; set; }
}
我的测试系统看起来像这样:
public void Execute(RequestA request)
{
var validDataIds = _someService.GetValidEntities().Select(x => x.Id).ToList();
var invalidRequestIds = request.RequestedDataIds.Where(requestedId => !validDataIds.Contains(requestedId));
if (invalidRequestIds.Any())
{
throw new SomeException("Error");
}
//Carry on as normal
}
我的问题是。是否有最佳实践或任何方式让 AutoFixture 让这两个集合相互交互,以便我可以进行测试:
- 测试当请求id无效时抛出异常
- 让所有其他测试确保它们相同,这样就不会抛出异常
- 在这种情况下甚至应该使用 AutoFixture 吗?我可以提取逻辑并使其成为 class 的新依赖项。必须然后只测试它被称为。但是,测试依赖项 class 本身仍然会引发问题 1 和 2。
这是我目前尝试解决的问题。我就是不喜欢。
1 - 确保不同的 id 抛出异常
[Test]
public void Test1()
{
_fixture = new Fixture();
var request = _fixture.Create<SomeRequest>();
var dataEnitities = _fixture.CreateMany<SomeData>();
var maxDataEntityId = dataEnitities.Select(x => x.Id).Max();
request.RequestedDataIds = new List<int> {maxDataEntityId + 1};
_someService.GetValidEntities.Returns(dataEnitities);
//Ensure exception is thrown
}
2 - 确保 ID 相同以防止异常
[Test]
public void Test1()
{
_fixture = new Fixture();
var request = _fixture.Create<SomeRequest>();
var dataEnitities = _fixture.CreateMany<SomeData>();
request.RequestedDataIds = dataEnitities.Select(x => x.Id);
_someService.GetValidEntities.Returns(dataEnitities);
//Ensure no exception is thrown
}
注意 - 此代码是实际实现的巨大缩减。因此,不必担心它 looks/is 是如何实现的。
是的,这是一种代码味道。您的测试是有效的,但它们可能很简单。 AutoFixture 是很棒的工具,但我认为在这种特定情况下您不需要它。
首先,尽量保持 Execute() 方法简单。它或多或少在 Presentation/Controller/Api 层中使用,因此尽可能保持精简。不想要任何胖控制器。
您真的只需要调用服务方法即可。
public void Execute(RequestA request)
{
_someService.ValidateEntities(request);
}
注意方法名称。它没有 return 任何东西。 您的服务可以是这样的:
public interface ISomeService
{
void ValidateEntities(RequestA request);
}
此实现将 return 来自底层数据源或服务的 ValidDataEntities。
var validDataIds = GetValidEntities().Select(x => x.Id);
我假设您从某种存储库或其他服务中获得了实体列表。
将您的逻辑推入更多 domain/poco 个对象。
创建一个简单的 POCO class 即 EntityValidator,它接受您的请求和 validDataId。 您可以只对这个 POCO class 进行单元测试,而无需使用 AutoFixture 或任何模拟。
public class EntityValidator
{
public void Validate(IEnumerable<int> validEntityIds, RequestA request)
{
var invalidRequestIds = request.RequestedDataIds.Where(requestedId => !validEntityIds.Contains(requestedId));
if (invalidRequestIds.Any())
{
throw new SomeException("Error");
}
}
}
您的单元测试将很简单,您可以使用测试框架方法,即 NUnit 中的 .Thorws 测试异常场景。