Skip to content

Commit

Permalink
Fix for issue #759. (#771)
Browse files Browse the repository at this point in the history
Close #759.
  • Loading branch information
Jim Przybylinski authored Aug 1, 2017
1 parent d747243 commit 6bcb34f
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Google.Apis.Requests;
using Google.Apis.Services;
using Moq;
using Moq.Language.Flow;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -38,75 +39,116 @@ public class DataSourceUnitTestsBase
/// <summary>
/// Gets a mock for a service that extends <see cref="BaseClientService"/>.
/// </summary>
/// <typeparam name="S">The type of service to mock.</typeparam>
/// <typeparam name="P1">The first resource type of the service.</typeparam>
/// <typeparam name="P2">The second resource type of the servcie.</typeparam>
/// <typeparam name="RequestType">The type of the request.</typeparam>
/// <typeparam name="ResponseType">The type of the response the request returns.</typeparam>
/// <param name="outerResourcePath">
/// <typeparam name="TService">The type of service to mock.</typeparam>
/// <typeparam name="TResource1">A resource type in the service.</typeparam>
/// <typeparam name="TResource2">A resource type in the outer resource type.</typeparam>
/// <typeparam name="TRequest">The type of the request.</typeparam>
/// <typeparam name="TResponse">The type of the response the request returns.</typeparam>
/// <param name="outerResourceExpression">
/// The path to the resource of the service. (e.g. (PubsubService service) => service.Projects)
/// </param>
/// <param name="innerResourcePath">
/// <param name="innerResourceExpression">
/// The path to the inner resource of the outer resource. (e.g. Projects => Projects.Topics)
/// </param>
/// <param name="requestMethod">
/// <param name="requestExpression">
/// The request expression on the second resource (e.g. Topics => Topics.List(It.IsAny&lt;string&gt;()))
/// </param>
/// <param name="parameterValues">
/// Parameter values to pretend the method was given.
/// </param>
/// <param name="responses">The list of reponses for the request to return.</param>
/// <returns>A mocked version of the service.</returns>
protected static S GetMockedService<S, P1, P2, RequestType, ResponseType>(
Expression<Func<S, P1>> outerResourcePath,
Expression<Func<P1, P2>> innerResourcePath,
Expression<Func<P2, RequestType>> requestMethod,
IEnumerable<object> parameterValues,
IEnumerable<ResponseType> responses)
where S : BaseClientService
where P1 : class
where P2 : class
where RequestType : ClientServiceRequest<ResponseType>
protected static TService GetMockedService<TService, TResource1, TResource2, TRequest, TResponse>(
Expression<Func<TService, TResource1>> outerResourceExpression,
Expression<Func<TResource1, TResource2>> innerResourceExpression,
Expression<Func<TResource2, TRequest>> requestExpression,
IEnumerable<TResponse> responses)
where TService : BaseClientService
where TResource1 : class
where TResource2 : class
where TRequest : ClientServiceRequest<TResponse>
{
var clientServiceMock = new Mock<IClientService>();
var serviceMock = new Mock<S>();
var outerResourceMock = new Mock<P1>(clientServiceMock.Object);
var innerResourceMock = new Mock<P2>(clientServiceMock.Object);
var requestMock = GetRequestMock<RequestType, ResponseType>(responses, parameterValues, clientServiceMock);

innerResourceMock.Setup(requestMethod).Returns(requestMock.Object);
outerResourceMock.Setup(innerResourcePath).Returns(innerResourceMock.Object);
serviceMock.Setup(outerResourcePath).Returns(outerResourceMock.Object);
IClientService clientService = GetMockedClientService<TRequest, TResponse>(responses);
TRequest request = GetMockedRequest<TRequest, TResponse>(requestExpression, clientService);

var innerResourceMock = new Mock<TResource2>(clientService);
innerResourceMock.Setup(requestExpression).Returns(request);

var outerResourceMock = new Mock<TResource1>(clientService);
outerResourceMock.Setup(innerResourceExpression).Returns(innerResourceMock.Object);

var serviceMock = new Mock<TService>();
serviceMock.Setup(outerResourceExpression).Returns(outerResourceMock.Object);
return serviceMock.Object;
}

/// <summary>
/// Gets a mock for a request that returns the given responce values.
/// Gets a mock for a service that extends <see cref="BaseClientService"/>.
/// </summary>
/// <typeparam name="RequestType">The type of request to mock.</typeparam>
/// <typeparam name="ResponeType">The type of the responce the request gives.</typeparam>
/// <param name="responses">The list of responses to return.</param>
/// <param name="additionalConstructorParams">
/// Any additional parameters the request type constructor requires.
/// <typeparam name="TService">The type of service to mock.</typeparam>
/// <typeparam name="TResource">A resource type of the service.</typeparam>
/// <typeparam name="TRequest">The type of the request.</typeparam>
/// <typeparam name="TResponse">The type of the response the request returns.</typeparam>
/// <param name="resourceExpression">
/// The path to a resource of the service. (e.g. (PubsubService service) => service.Projects)
/// </param>
/// <param name="clientServiceMock">
/// The mock of the <see cref="IClientService"/> for the requests to use.
/// <param name="requestExpression">
/// The request expression on the resource (e.g. Topics => Topics.List(It.IsAny&lt;string&gt;()))
/// </param>
/// <returns>A mock of the given request type.</returns>
private static Mock<RequestType> GetRequestMock<RequestType, ResponeType>(
IEnumerable<ResponeType> responses,
IEnumerable<object> additionalConstructorParams,
Mock<IClientService> clientServiceMock)
where RequestType : ClientServiceRequest<ResponeType>
/// <param name="responses">The list of reponses for the request to return.</param>
/// <returns>A mocked version of the service.</returns>
protected static TService GetMockedService<TService, TResource, TRequest, TResponse>(
Expression<Func<TService, TResource>> resourceExpression,
Expression<Func<TResource, TRequest>> requestExpression,
IEnumerable<TResponse> responses)
where TService : BaseClientService
where TResource : class
where TRequest : ClientServiceRequest<TResponse>
{

IClientService clientService = GetMockedClientService<TRequest, TResponse>(responses);
TRequest request = GetMockedRequest<TRequest, TResponse>(requestExpression, clientService);

var resourceMock = new Mock<TResource>(clientService);
resourceMock.Setup(requestExpression).Returns(request);

var serviceMock = new Mock<TService>();
serviceMock.Setup(resourceExpression).Returns(resourceMock.Object);
return serviceMock.Object;
}

private static TRequest GetMockedRequest<TRequest, TResponse>(
LambdaExpression requestExpression,
IClientService clientService)
where TRequest : ClientServiceRequest<TResponse>
{
var requestMethod = requestExpression.Body as MethodCallExpression;
if (requestMethod == null)
{
throw new ArgumentException(
$"{nameof(requestExpression)}.{nameof(requestExpression.Body)} " +
$"must be of type {nameof(MethodCallExpression)} " +
$"but was {requestExpression.Body.GetType()}");
}

IEnumerable<object> methodArgs =
requestMethod.Arguments.Select(a => a.Type)
.Select(Expression.Default)
.Select(e => Expression.Convert(e, typeof(object)))
.Select(e => Expression.Lambda<Func<object>>(e).Compile()());
object[] constructorArgs = new[] { clientService }.Concat(methodArgs).ToArray();
var requestMock = new Mock<TRequest>(constructorArgs)
{
CallBase = true
};

requestMock.Setup(r => r.RestPath).Returns("/");
requestMock.Object.RequestParameters.Clear();
return requestMock.Object;
}

private static IClientService GetMockedClientService<TRequest, TResponse>(IEnumerable<TResponse> responses)
where TRequest : ClientServiceRequest<TResponse>
{
var requestMock =
new Mock<RequestType>(
Enumerable.Repeat(clientServiceMock.Object, 1).Concat(additionalConstructorParams).ToArray())
{
CallBase = true
};

var handlerMock = new Mock<HttpMessageHandler>();
var clientServiceMock = new Mock<IClientService>();
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
// Use MockBehavior.Strict to ensure we make no acutal http requests.
var configurableHandlerMock =
new Mock<ConfigurableMessageHandler>(MockBehavior.Strict, handlerMock.Object);
Expand All @@ -119,22 +161,20 @@ private static Mock<RequestType> GetRequestMock<RequestType, ResponeType>(
clientServiceMock.Setup(c => c.Serializer.Format).Returns("json");
clientServiceMock.Setup(c => c.SerializeObject(It.IsAny<object>())).Returns("{}");

var deserializeSetup =
clientServiceMock.Setup(c => c.DeserializeResponse<ResponeType>(It.IsAny<HttpResponseMessage>()));
ISetup<IClientService, Task<TResponse>> deserializeSetup =
clientServiceMock.Setup(c => c.DeserializeResponse<TResponse>(It.IsAny<HttpResponseMessage>()));
var responseQueue =
new Queue<Task<ResponeType>>(
responses?.Select(Task.FromResult) ?? Enumerable.Empty<Task<ResponeType>>());
new Queue<Task<TResponse>>(
responses?.Select(Task.FromResult) ?? Enumerable.Empty<Task<TResponse>>());
if (responseQueue.Count == 0)
{
deserializeSetup.Throws(new GoogleApiException(typeof(RequestType).FullName, MockExceptionMessage));
deserializeSetup.Throws(new GoogleApiException(typeof(TRequest).FullName, MockExceptionMessage));
}
else
{
deserializeSetup.Returns(responseQueue.Dequeue);
}
requestMock.Setup(r => r.RestPath).Returns("/");
requestMock.Object.RequestParameters.Clear();
return requestMock;
return clientServiceMock.Object;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -57,6 +57,10 @@
<HintPath>..\packages\Google.Apis.Auth.1.26.2\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Google.Apis.CloudResourceManager.v1, Version=1.26.2.874, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.CloudResourceManager.v1.1.26.2.874\lib\net45\Google.Apis.CloudResourceManager.v1.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.26.2.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Core.1.26.2\lib\net45\Google.Apis.Core.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -93,6 +97,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="ResourceManagerDataSourceUnitTests.cs" />
<Compile Include="DataSourceUnitTestsBase.cs" />
<Compile Include="PubSubDataSourceUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand All @@ -105,7 +110,9 @@
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
<None Include="packages.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="README.md" />
</ItemGroup>
<Choose>
Expand Down Expand Up @@ -135,4 +142,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ namespace GoogleCloudExtension.DataSources.UnitTests
[TestClass]
public class PubSubDataSourceUnitTests : DataSourceUnitTestsBase
{
private const string DummyString = "DummyString";
private const string MockedTopicName = "MockedTopicName";
private const string MockedSubscriptionName = "MockedSubscriptionName";
private const string FirstName = "FirstName";
Expand Down Expand Up @@ -61,7 +60,6 @@ public async Task TestGetTopicListAsyncSinglePage()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.List(It.IsAny<string>()),
new[] { DummyString },
responses);

var sourceUnderTest = new PubsubDataSource(service, ProjectName);
Expand Down Expand Up @@ -93,7 +91,7 @@ public async Task TestGetTopicListAsyncMultiPage()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.List(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);

var sourceUnderTest = new PubsubDataSource(service, ProjectName);
IList<Topic> topics = await sourceUnderTest.GetTopicListAsync();
Expand All @@ -115,7 +113,7 @@ public async Task TestGetTopicListAsyncException()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.List(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand Down Expand Up @@ -149,7 +147,7 @@ public async Task TestGetSubscriptionListAsyncSinglePage()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.List(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

IList<Subscription> subscriptions = await sourceUnderTest.GetSubscriptionListAsync();
Expand Down Expand Up @@ -180,7 +178,7 @@ public async Task TestGetSubscriptionListAsyncPaged()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.List(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

IList<Subscription> subscriptions = await sourceUnderTest.GetSubscriptionListAsync();
Expand All @@ -202,7 +200,7 @@ public async Task TestGetSubscriptionListAsyncException()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.List(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand All @@ -226,7 +224,7 @@ public async Task TestNewTopicAsyncSuccess()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.Create(It.IsAny<Topic>(), It.IsAny<string>()),
new object[] { new Topic(), DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

var topic = await sourceUnderTest.NewTopicAsync(TopicName);
Expand All @@ -246,7 +244,7 @@ public async Task TestNewTopicAsyncException()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.Create(It.IsAny<Topic>(), It.IsAny<string>()),
new object[] { new Topic(), DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand All @@ -270,7 +268,7 @@ public async Task TestDeleteTopicAsyncSuccess()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.Delete(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

await sourceUnderTest.DeleteTopicAsync(TopicFullName);
Expand All @@ -289,7 +287,7 @@ public async Task TestDeleteTopicAsyncException()
(PubsubService s) => s.Projects,
p => p.Topics,
t => t.Delete(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand All @@ -313,7 +311,7 @@ public async Task TestNewSubscriptionAsyncSuccess()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.Create(It.IsAny<Subscription>(), It.IsAny<string>()),
new object[] { new Subscription(), DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

var subscription = await sourceUnderTest.NewSubscriptionAsync(s_newSubscription);
Expand All @@ -334,7 +332,7 @@ public async Task TestNewSubscriptionAsyncException()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.Create(It.IsAny<Subscription>(), It.IsAny<string>()),
new object[] { new Subscription(), DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand All @@ -358,7 +356,7 @@ public async Task TestDeleteSubscriptionAsyncSuccess()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.Delete(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

await sourceUnderTest.DeleteSubscriptionAsync(SubscriptionFullName);
Expand All @@ -377,7 +375,7 @@ public async Task TestDeleteSubscriptionAsyncException()
(PubsubService s) => s.Projects,
p => p.Subscriptions,
s => s.Delete(It.IsAny<string>()),
new[] { DummyString }, responses);
responses);
var sourceUnderTest = new PubsubDataSource(service, ProjectName);

try
Expand Down
Loading

0 comments on commit 6bcb34f

Please sign in to comment.