I am using the Microsoft Fakes unit testing framework for testing some methods that make queries to a DocumentDB database.
The DocumentClient class has several methods for making queries to DocDB (such as CreateDocumentAsync()) which return a ResourceResponse<Document> object wrapped up in a Task<T>.
I would like to shim CreateDocumentAsync() for unit testing purposes, however the return type, ResourceResponse<T>, doesn't appear to have a public constructor, despite mention of one in the documentation.
An extremely simplified version of what I want to accomplish is here:
[TestMethod]
public async Task MyTest() {
using (ShimsContext.Create()) {
// Arrange
var docClient = new DocumentClient(new Uri("myUri"), "myKey");
ShimDocumentClient.AllInstances.CreateDocumentAsyncUriObjectRequestOptionsBoolean =
(DocumentClient instance, Uri uri, object document, RequestOptions options, bool disableAutomaticGeneration) =>
{
ResourceResponse<Document> response = new ResourceResponse<Document>(); // "error: does not contain a constructor that takes zero arguments"
return response ;
};
// Act
var response = await docClient.CreateDocumentAsync(new Uri("myCollectionUri"), "myDoc");
// Assert
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
}
}
How can I create a custom ResrouceResponse<Document> object to return in the shimmed method?
As noted in comment. v1.10 of the SDK supports ResourceResponse constructor will no arguments. The packages.config in the project solution should show the version of DocumentDB SDK the project is using:
Related
I am new to this MOQ framework and honestly having a hard time with having to get my unit test to run. Basically, I have a C# application which basically does some uploads to APIs using PostAsync.
Now, since I can't (and should not) call the API during my unit test (as otherwise it would be an integration test), I added a wrapper method around it and allowing that method to return true by mocking it. But no matter what I do, it is returning false. I have gone through SO questions, but I am not sure what am I missing. I haven't used interfaces but am using classes with virtual methods.
Here is my sample code that I would want to test
public async Task<bool> CreateNoteBookDirectory (string url ,string bearertoken, JavaScriptSerializer jser,RestPostClass rest)
{
NoteBookDirectory jsnbdir = new NoteBookDirectory();
jsnbdir.path = "/JobNotebooks/ClientScoreDataInput";
var directorycreate = jser.Serialize(jsnbdir);
var content = new StringContent(directorycreate, Encoding.UTF8, #"application/json");
bool result=await rest.HttpPost(url, content, bearertoken);
return result;
}
This method is in the main class.
The RestPostClass class has the virtual method HttpPost, whose skeleton is somewhat like this
public async virtual Task<bool> HttpPost(String url, StringContent content, string bearertoken)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearertoken);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(#"application/json"));
var postresult = await client.PostAsync(url, content);
bool result = parseResponse(postresult);
return result;
}
Now, in my unit test, I am trying to test the CreateNoteBookDirectory method and since do not want the post method to be called, mocking it.
Here is what I am doing in my sample unit test
Mock<DataBricksRestPost> mock = new Mock<DataBricksRestPost>();
mock.Setup(x => x.HttpPost("http://test.com", new StringContent("abc"), "token")).Returns(Task.FromResult(true));
Program prog = new Program();
var jser = new JavaScriptSerializer();
bool result= await prog.CreateNoteBookDirectory("http://test.com", "token", jser, mock.Object);
Assert.IsTrue(result, "Test failed");
It keeps returning false because apparently the mocking does not really happen properly.
What am I missing?
Any questions and I will try my best to clarify.
P.S: I have used the existing the "program" class as I am basically starting.
Mock returns false, because when you call HttpPost parameters don't match with ones that were set up. The second parameter is different.
You can set up mock like that:
mock
.Setup(x => x.HttpPost("http://test.com", It.IsAny<StringContent>(), "token"))
.Returns(Task.FromResult(true)); //or .ReturnsAsync(true);
It tells mocking framework, that second parameter can be any object of type StringContent.
Docs can be found here: https://github.com/Moq/moq4/wiki/Quickstart#matching-arguments
Hi I am working on C# application. I am writing unit test case for controller level. Below is my controller code.
public IActionResult TriggerProductEventCheck([FromQuery(Name = "timeout-secs")] int timeoutS = 120)
{
int productEventsCount = 0;
if (productService.ProcessingEnabled)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(timeoutS * 1000);
lock (SyncObject)
{
this.consumerClient.Subscribe(this.productEventTopicName);
while (!cts.IsCancellationRequested)
{
var productEvent = this.eventDispatcher.Consume();
long kafkaOffSet = productEvent.Offset.Value;
Product product = new Product(productEvent, log);
if (product.Options == null)
{
break;
}
if (product != null)
{
productService.ProcessProductEvents(product, kafkaOffSet);
}
productEventsCount++;
}
}
}
Below is my unit test case.
public void ShouldReturnIfNoSQSEvents()
{
var productEventController = MockProvider.Target<ProductEventController>();
productEventController.GetDependency<IProductEventService>().ProcessingEnabled.Returns(true);
productEventController.GetDependency<IEventDispatcher>().Consume().Returns(new Confluent.Kafka.ConsumeResult<string, Avro.Generic.GenericRecord>());
productEventController.GetDependency<IConsumerClient>().Subscribe("test");
var productclass = Substitute.For<Product>();
var response = productEventController.Target.TriggerProductEventCheck() as JsonResult;
((int)response.StatusCode).ShouldBe(200);
}
Whenever I run above unit test case, Control goes inside Product product = new Product(productEvent, log); I want to mock this particular line. May I know how to handle this condition? Any help would be appreciated. Thanks
There are at least two ways.
The most advisable: add a second method that allows you to inject the desired elements. You can call one method from the other to reuse code. This second method would be used for testing. The other shuld be left as is.
Another option is to use a framework that allows to implement shims for testing. For example:
- Moles, that "allows to replace any .NET method with a delegate". Here you can see an example that allows to mock DateTime constructor: Nikolai Tillmann: Moles - Replace any .NET method with a delegate.
- "Microsot fakes": Isolating Code Under Test with Microsoft Fakes.
- There are some commercial testing frameworks that also support shims.
However, you should use the first proposed solution, or change the desing of your software, for example using Dependency Injection, so that you can easily control de dependencies in your unit testing. You should leave the use of shims for cases where you don't have any other option, for example injecting changes in third party libraries for which you don't have the option to modify their code.
You can google ".NET shims testing" to learn more about this technique.
So far I've been able to setup unit testing for Azure Functions and it works great. However for my current project I need to use dynamic or imperative bindings.
https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#imperative-bindings
This leads to issues for my unit test I cannot seem to solve.
My function looks like this:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.ServiceBus.Messaging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace My.Functions
{
public static class MyFunc
{
[FunctionName("my-func")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req,
Binder binder)
{
dynamic data = await req.Content.ReadAsAsync<object>();
byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
MemoryStream stream = new MemoryStream(bytes, writable: false);
var sbMsg = new BrokeredMessage(stream) { ContentType = "application/json" };
var attributes = new Attribute[]
{
new ServiceBusAccountAttribute("some-sb-account"),
new ServiceBusAttribute("some-queue-or-topic", AccessRights.Send)
};
var outputSbMessage = await binder.BindAsync<IAsyncCollector<BrokeredMessage>>(attributes);
await outputSbMessage.AddAsync(sbMsg);
return req.CreateResponse(HttpStatusCode.OK, "OK");
}
}
}
Near the end of the code of the function, I configure this binder to hold a list of BrokeredMessages. This is done by calling the BindAsync on the binder.
The attributes are dynamically set and contain a servicebus connection and topic name. This all works great when deployed to Azure so functionality-wise everything is fine.
So far so good.
However I'm stuggling with getting my test running. To be able to invoke the function, I need to provide parameters. The HttpTrigger this is pretty common, but for the Binder I don't know what to provide.
For testing I use this approach:
[TestMethod]
public void SendHttpReq()
{
// Setup
var httpRequest = GetHttpRequestFromTestFile("HttpRequest");
var sbOutput = new CustomBinder();
// Act
var response = SendToServicebus.Run(httpRequest, sbOutput);
// Assert
Assert.AreEqual(sbOutput.Count(), 1);
// Clean up
}
I use a CustomBinder inherited from Binder, because just having an instance of Binder failed in the function on the 'BindAsync' throwing 'Object reference not set to an instance of an object'. It seems the constructor of the binder is actually not meant to be called.
In the CustomBinder I override the BindAsync to return a generic list of BrokeredMessages.
public class CustomBinder : Binder
{
public override async Task<TValue> BindAsync<TValue>(Attribute[] attributes, CancellationToken cancellationToken = new CancellationToken())
{
return (TValue)((object)(new List<BrokeredMessage>()));
}
}
Not entirely surprising that also failed throwing:
InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List'1[Microsoft.ServiceBus.Messaging.BrokeredMessage]' to type 'Microsoft.Azure.WebJobs.IAsyncCollector`1[Microsoft.ServiceBus.Messaging.BrokeredMessage]'.
I cannot find an implementation of the IAsyncCollector, so maybe I need to approach this differently?
My actual goal is to be able to verify the list of brokered messages, as the function would output to Azure servicebus.
As mentioned in the comments, I would agree that mocking it makes sense. You explicitely want to unit test your own code logic. With only your own business logic in mind, you may assume that the actual the actual remote operation binder.BindAsync(...) - of which you have no control over - works as expected.
Mocking it in a unit test should work with something like this:
using FluentAssertions;
using Microsoft.Azure.WebJobs;
using Microsoft.ServiceBus.Messaging;
using Xunit;
[Fact]
public async Task AzureBindAsyncShouldRetrunBrokeredMessage()
{
// arrange
var attribute = new ServiceBusAccountAttribute("foo");
var mockedResult = new BrokeredMessage()
{
Label = "whatever"
};
var mock = new Mock<IBinder>();
mock.Setup(x => x.BindAsync<BrokeredMessage>(attribute, CancellationToken.None))
.ReturnsAsync(mockedResult);
// act
var target = await mock.Object.BindAsync<BrokeredMessage>(attribute);
// assert
target.Should().NotBeNull();
target.Label.Should().Be("whatever");
}
I understand that your concern may be a full integration test. You seem to want to test the entire chain. In that case, having a unit test might prove difficult because you depend on an external system. If that is the case you might want to create a seperate integration test on top of it, by setting up a seperate instance.
Considering your function is setup as a HttpTrigger, the following should work:
# using azure functions cli (2.x), browse to the output file
cd MyAzureFunction/bin/Debug/netstandard2.0
# run a new host/instance if your function
func host start
Next, simply execute a http request to the hosted endpoint:
$ [POST] http://localhost:7071/api/HttpTriggerCSharp?name=my-func
In this case you have a clean and isolated integration setup.
Either way, I'd like to argue to either go the route of the unit test with mock OR setting up a seperate integration test setup for it.
Hope this helps...
I have build a WebAPI and want to create a unit test project to have my services tested automatically.
The flow of my WebAPI is simple:
Controller (DI Service) -> Service (DI Repository) -> _repo CRUD
Suppose I have a service like:
public int Cancel(string id) //change status filed to 'n'
{
var item = _repo.Find(id);
item.status = "n";
_repo.Update(item);
return _repo.SaveChanges();
}
And I want to build a unit test, which just use InMemoryDatabase.
public void Cancel_StatusShouldBeN() //Testing Cancel() method of a service
{
_service.Insert(item);
int rs = _service.Cancel(item.Id);
Assert.Equal(1, rs);
item = _service.GetByid(item.Id);
Assert.Equal("n", item.status);
}
I've searched other related question, found that
You can't use dependency injections on test classes.
I just want to know if there is any other solution to achive my unit test idea?
When unit testing, you should just supply all the dependencies of the class you are testing explicitly. That is dependency injection; not having the service construct its dependencies on its own but making it rely on the outer component to provide them. When you are outside of a dependency injection container and inside a unit test where you are manually creating the class you are testing, it’s your responsibility to provide the dependencies.
In practice, this means that you either provide mocks or actual objects to the constructor. For example, you might want to provide a real logger but without a target, a real database context with a connected in-memory database, or some mocked service.
Let’s assume for this example, that the service you are testing looks like this:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
So in order to test ExampleService, we need to provide those three objects. In this case, we will do the following for each:
ILogger<ExampleService> – we will use a real logger, without any attached target. So any call on the logger will work properly without us having to provide some mock, but we do not need to test the log output, so we do not need a real target
MyDbContext – Here, we’ll use the real database context with an attached in-memory database
UtilityService – For this, we will create a mock which just setups the utility method we need inside the methods we want to test.
So a unit test could look like this:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
In your specific Cancel case, you want to avoid using any methods of the service you are not currently testing. So if you want to test Cancel, the only method you should call from your service is Cancel. A test could look like this (just guessing the dependencies here):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw. note that I’m opening up a new database context all the time in order to avoid getting results from the cached entities. By opening a new context, I can verify that the changes actually made it into the database completely.
I have a fairly simple class that I'm trying to unit test. I'm very new to unit testing in general, and I'm not sure what I should be testing here.
The only test case that I can figure out how to code is a null argument of stream. Besides that, I'm not sure how to test the results of a PutObjectRequest or what else. If I should be using mocks here, how?
public class AmazonS3Service : IAmazonS3Service
{
private readonly Uri baseImageUrl;
private readonly Uri s3BaseUrl;
private readonly string imageBucket;
public AmazonS3Service()
{
imageBucket = ConfigurationManager.AppSettings["S3.Buckets.Images"];
s3BaseUrl = new Uri(ConfigurationManager.AppSettings["S3.BaseAddress"]);
baseImageUrl = new Uri(s3BaseUrl, imageBucket);
}
public Image UploadImage(Stream stream)
{
if (stream == null) throw new ArgumentNullException("stream");
var key = string.Format("{0}.jpg", Guid.NewGuid());
var request = new PutObjectRequest
{
CannedACL = S3CannedACL.PublicRead,
Timeout = -1,
ReadWriteTimeout = 600000, // 10 minutes * 60 seconds * 1000 milliseconds
InputStream = stream,
BucketName = imageBucket,
Key = key
};
using (var client = new AmazonS3Client())
{
using (client.PutObject(request))
{
}
}
return new Image
{
UriString = Path.Combine(baseImageUrl.AbsoluteUri, key)
};
}
}
You are having trouble unit testing UploadImage because it is coupled to many other external services and state. Static calls including (new) tightly couple the code to specific implementations. Your goal should be to refactor those so that you can more easily unit test. Also, keep in mind that after unit testing this class, you will still need to do the big tests involving actually using the Amazon S3 service and making sure the upload happened correctly without error or fails as expected. By unit testing thoroughly, hopefully you reduce the number of these big and possibly expensive tests.
Removing the coupling to the AmazonS3Client implementation is probably going to give you the biggest bang for your testing buck. We need to refactor by pulling out the new AmazonS3Client call. If there is not already an interface for this class, then I would create one to wrap it. Then you need to decide how to inject the implementation. There are a number of options, including as a method parameter, constructor parameter, property, or a factory.
Let's use the factory approach because it is more interesting than the others, which are straight-forward. I've left out some of the details for clarity and read-ability.
interface IClientFactory
{
IAmazonS3Client CreateAmazonClient();
}
interface IAmazonS3Client
{
PutObjectResponse PutObject(PutObjectRequest request); // I'm guessing here for the signature.
}
public class AmazonS3Service : IAmazonS3Service
{
// snip
private IClientFactory factory;
public AmazonS3Service(IClientFactory factory)
{
// snip
this.factory = factory;
}
public Image UploadImage(Stream stream)
{
if (stream == null) throw new ArgumentNullException("stream");
var key = string.Format("{0}.jpg", Guid.NewGuid());
var request = new PutObjectRequest
{
CannedACL = S3CannedACL.PublicRead,
Timeout = -1,
ReadWriteTimeout = 600000, // 10 minutes * 60 seconds * 1000 milliseconds
InputStream = stream,
BucketName = imageBucket,
Key = key
};
// call the factory to provide us with a client.
using (var client = factory.CreateAmazonClient())
{
using (client.PutObject(request))
{
}
}
return new Image
{
UriString = Path.Combine(baseImageUrl.AbsoluteUri, key)
};
}
}
A unit test might look like this in MSTest:
[TestMethod]
public void InputStreamSetOnPutObjectRequest()
{
var factory = new TestFactory();
var service = new AmazonS3Service(factory);
using (var stream = new MemoryStream())
{
service.UploadImage(stream);
Assert.AreEqual(stream, factory.TestClient.Request.InputStream);
}
}
class TestFactory : IClientFactory
{
public TestClient TestClient = new TestClient();
public IAmazonS3Client CreateClient()
{
return TestClient;
}
}
class TestClient : IAmazonS3Client
{
public PutObjectRequest Request;
public PutObjectResponse Response;
public PutObjectResponse PutObject(PutObjectRequest request)
{
Request = request;
return Response;
}
}
Now, we have one test verifying that the correct input stream is sent over in the request object. Obviously, a mocking framework would help cut down on a lot of boilerplate code for testing this behavior. You could expand this by starting to write tests for the other properties on the request object. Error cases are where unit testing can really shine because often they can be difficult or impossible to induce in production implementation classes.
To fully unit test other scenarios of this method/class, there are other external dependencies here that would need to be passed in or mocked. The ConfigurationManager directly accesses the config file. Those settings should be passed in. Guid.NewGuid is basically a source of uncontrolled randomness which is also bad for unit testing. You could define an IKeySource to be a provider of key values to various services and mock it or just have the key passed from the outside.
Finally, you should be weighing all the time taken for testing/refactoring against how much value it is giving you. More layers can always be added to isolate more and more components, but there are diminishing returns for each added layer.
Things I would look at:
Mock your configuration manager to return invalid data for the bucket and the URL. (null, invalid urls, invalid buckets)
Does S3 support https ? If so mock it, if not, mock it and verify you get a valid error.
Pass different kinds of streams in (Memory, File, other types).
Pass in streams in different states (Empty streams, streams that have been read to the
end, ...)
I would allow the timeouts to be set as parameters, so you can test with really low
timeouts and see what errors you get back.
I would also test with duplicate keys, just to verify the error message. Even though you are using guids, you are storing to an amazon server where someone else could use the S3 API to store documents and could theoretically create a file that appears to be a guid, but could create a conflict down the road (unlikely, but possible)