How can I perform additional object configuration via parameter? - c#

I'm writing unit tests where the SUT (system under test) can undertake a wide variety of different configurations. Instead of adding a parameter for each combination, I decided to (or attempt) take an alternative, cleaner approach. For those that don't know, the Fixture object is from a library called AutoFixture, which handles seeding random test values.
Here is my failing test -
[Test]
public void Test()
{
var locate = Build(x => x.With(xx => xx.TicketNo, 123));
Assert.AreEqual(123, locate.TicketNo);
}
private Locate Build(Action<ICustomizationComposer<Locate>> customizationAction)
{
var fixture = new Fixture();
var customizationComposer = fixture.Build<Locate>();
customizationAction(customizationComposer);
var postProcessComposer = customizationComposer
.Without(x => x.Attachments)
.Without(x => x.Comments)
.Without(x => x.Reviews)
.Without(x => x.ScheduledCrew)
.Without(x => x.PendingDecision)
.Without(x => x.FinalDecision)
.Without(x => x.ConflictResolution);
return postProcessComposer.Create();
}
As you can, I'm attempting to use the Build method to create a Locate object with a set of ignore rules via the Without method calls. This is the baseline object I require for each test. Each test has the option to ignore additional fields or hard code field values via the With method call.
I would expect to pass because I set my Build lambda parameter to set the TicketNo property to 123. Instead, the test fails because AutoFixture seeds TicketNo with a random integer.
How can I achieve this?

Solution!
private Locate Build(Func<IPostprocessComposer<Locate>, IPostprocessComposer<Locate>> action)
{
var customizationComposer = _fixture.Build<Locate>();
var postProcessComposer = customizationComposer
.With(x => x.TicketNo, DateTime.Now.ToFileTime())
.With(x => x.On1CallNotified, false)
.Without(x => x.Attachments)
.Without(x => x.Comments)
.Without(x => x.Reviews)
.Without(x => x.ScheduledCrew)
.Without(x => x.PendingDecision)
.Without(x => x.FinalDecision)
.Without(x => x.ConflictResolution);
postProcessComposer = action(postProcessComposer);
return postProcessComposer.Create();
}

Related

Unit testing a specific MongoDb FilterDefintion in C# with Moq

Trying to unit test the following MongoDb update function in C#.
var update = Builders<InvestorMongoDao>.Update
.Set(x => x.EmailAddress, command.EmailAddress);
await this.myDatabase.Users
.UpdateOneAsync(x => x.UserId == userId), update)
.ConfigureAwait(false);
I can easily check whether this function is called with a generic filter:
this.mockCollection
.Verify(x => x.UpdateOneAsync(It.IsAny<FilterDefinition<MyDao>>(),
It.IsAny<UpdateDefinition<MyDao>>(),
It.IsAny<UpdateOptions>(),
It.IsAny<CancellationToken>()));
However, I want to try and verify that it's being called with the expected parameter. I cannot seem to find a way to extract the parameter I want to check from FilterDefinition. I have tried this:
var foo = Builders<MyDao>.Filter.Eq("UserId", expectedUserId);
this.mockCollection
.Verify(x => x.UpdateOneAsync(It.Is<FilterDefinition<MyDao>>(a => a == foo),
It.IsAny<UpdateDefinition<MyDao>>(),
It.IsAny<UpdateOptions>(),
It.IsAny<CancellationToken>()));
But the test states the invocation is not performed. What am I doing wrong?
It appears you can approximate what's needed here by using Render.
var serializerRegistry = BsonSerializer.SerializerRegistry;
var documentSerializer = serializerRegistry.GetSerializer<MyDao>();
var expectedFilter = Builders<MyDao>.Filter.Eq("UserId", existingInvestor2.InvestorId);
this.mockCollection
.Verify(x => x.UpdateOneAsync(It.Is<FilterDefinition<MyDao>>(a => a.Render(documentSerializer, serializerRegistry) == expectedFilter.Render(documentSerializer, serializerRegistry)),
It.IsAny<UpdateDefinition<MyDao>>(),
It.IsAny<UpdateOptions>(),
It.IsAny<CancellationToken>()));

How to mock Elasticsearch .Net client?

I am using Elasticsearch .Net Client 6.x, and I have some code like:
if(this.elasticClient.IndexExists("indexName").Exists){
// do something
}
this.elasticClient.CreateIndex(
newIndex,
x => x.Settings(s => s.NumberOfShards(1)).Mappings(ms => ms.Map<T>(m => m.AutoMap())));
var searchResponse = client.Search<dynamic>(
s => s.AllTypes()
.Index(
new[] { "indexName1", "indexName"}).IgnoreUnavailable().Size(size).From(from).Query(
q => q.Bool(b => b.Must(m => m.SimpleQueryString(c => c.Query(query).Lenient().AnalyzeWildcard())))));
Now in my unit test, I'd like to test the exact descriptor passed to each method, how can I do this?
var existsResponse = new Mock<ExistsResponse>();
existsResponse.Setup(x => x.Exists).Returns(false); // Oops: can't do this since Exists is not virtual, abstract
this.elasticClient.Setup(x => x.IndexExists("indexName", null)).Returns(existsResponse.Object);
this.elasticClient.Verify(
x => x.CreateIndex("IndexName", It.Is<Func<CreateIndexDescriptor, ICreateIndexRequest>>(x => /*what goes here??*/)),
Times.Once);
this.elasticClient.Verify(
x => x.Search<dynamic>(It.Is<Func<SearchDescriptor<dynamic>>>(x => /*what goes here??*/)),
Times.Once);
Should I just simply wrap the elasticClient in a class and implement every method I used, and pass it as dependency injection?

Automapper for member condition

I am using auto mapper 6.1 and I want to map some values from one object to another, but there is a condition that those values can not be null and not all object properties are supposed to be mapped if so I could easily use ForAllMembers conditions. What I am trying to do is:
config.CreateMap<ClassA, ClassB>()
.ForMember(x => x.Branch, opt => opt.Condition(src => src.Branch != null),
cd => cd.MapFrom(map => map.Branch ?? x.Branch))
Also tried
config.CreateMap<ClassA, ClassB>().ForMember(x => x.Branch, cd => {
cd.Condition(map => map.Branch != null);
cd.MapFrom(map => map.Branch);
})
In another words for every property I define in auto mapper configuration I want to check if its null, and if it is null leave value from x.
Call for such auto mapper configuration would look like:
ClassA platform = Mapper.Map<ClassA>(classB);
If I've understood correctly, it may be simpler than you think. The opt.Condition is not necessary because the condition is already being taken care of in MapFrom.
I think the following should achieve what you want: it will map Branch if it's not null. If Branch (from the source) is null, then it will set the destination to string.Empty.
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => map.Branch ?? string.Empty));
And if you need to use another property from x instead of string.Empty, then you can write:
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => map.Branch ?? x.AnotherProperty));
If you want to implement complex logic but keep the mapping neat, you can extract your logic into a separate method. For instance:
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => MyCustomMapping(map)));
private static string MyCustomMapping(ClassA source)
{
if (source.Branch == null)
{
// Do something
}
else
{
return source.Branch;
}
}
You don't need the MapFrom, but you need a PreCondition instead. See here.

Managing state in a reactive pipeline

I am constructing a reactive pipeline that needs to expand (SelectMany) and then flatten (in this case, ToArray) whilst maintaining access to a piece of state obtained at the beginning of the pipeline.
Here is pseudo-code for what I am attempting:
return Observable
.Start(() => this.GetSearchResults(query))
.SelectMany(results => results.Hits) // results.Hits is a list of IDs. But there is also has a bool property that I want to keep through to the end of my pipeline
.SelectMany(hit => GetById(hit.Id)) // asynchronously load each result
.ToArray() // now need to pull all the results together into a containing data structure, and also include the bool flag from above in it
.Select(resolvedResults => new ...); // need access to both resolvedResults and the bool mentioned in the first comment above
So I'm trying to find a way to cleanly access some state determined at the beginning of the pipeline from the code at the end of the pipeline.
The first thing I tried was using anonymous types to bundle the bool with each result. This quickly got out of hand and was wasteful from a performance perspective.
The second thing I tried was using a subject as follows:
var state = new AsyncSubject<bool>();
return Observable
.Start(() => this.GetSearchResults(query))
.Do(results =>
{
state.OnNext(results.Flag);
state.OnCompleted();
}
.SelectMany(results => results.Hits)
.SelectMany(hit => GetById(hit.Id))
.ToArray()
.Zip(
state,
(results, state) => new ResultContainer(state, results));
This seems to work fine, but feels a little icky to me.
So what I'm wondering is whether there is a cleaner way to manage state in a reactive pipeline.
For reference, here is the actual code (rather than just pseudo-code):
public IObservable<ISearchResults<IContact>> Search(string query, int maximumResultCount = 100, float minimumScore = 0.1F)
{
Ensure.ArgumentNotNull(query, nameof(query));
var moreHitsAvailable = new AsyncSubject<bool>();
return Observable
.Start(
() => this.searchIndexService.Search<IContact>(query, maximumResultCount, minimumScore),
this.schedulerService.DataStoreScheduler)
.Do(
results =>
{
moreHitsAvailable.OnNext(results.MoreHitsAreAvailable);
moreHitsAvailable.OnCompleted();
})
.SelectMany(
results => results
.Hits
.Select(
hit => new
{
Id = hit.Id,
ParsedId = ContactId.Parse(hit.Id)
}))
.SelectMany(
result => this
.GetById(result.ParsedId)
.Select(
contact => new
{
Id = result.Id,
Contact = contact
}))
.Do(
result =>
{
if (result.Contact == null)
{
this.logger.Warn("Failed to find contact with ID '{0}' provided by the search index. Index may be out of date.", result.Id);
}
})
.Select(result => result.Contact)
.Where(contact => contact != null)
.ToArray()
.Zip(
moreHitsAvailable,
(results, more) => new SearchResults<IContact>(more, results.ToImmutableList()))
.PublishLast()
.ConnectUntilCompleted();
}
You could pop out to Query Comprehension Syntax and do something like this
var x = from result in Observable.Start(() => this.GetSearchResults())
let hasMore = result.MoreHitsAreAvailable
from hit in result.Hits
from contact in GetById(hit.Id)
select new { hasMore , contact};
Over to you how to deal with the duplicate hasMore values. As we know it will be just the single distinct value (all true or all false) you could group by.

Moq EF 6 DBSet, Setup FirstOrDefault method

I would like testing private method. When I setup FirstOrDefault method Moq thrown Exception. In my algorithm, I want check that, does my method properly create new objects.
See my code below.
//Arrange
var data = new List<TypeValue>();
var typeValueMockSet = new Mock<DbSet<TypeValue>>();
typeValueMockSet
.As<IList<TypeValue>>()
.Setup(m => m.GetEnumerator())
.Returns(data.GetEnumerator());
//throw the Error => Expression references a method that does not belong to the mocked object:
// m => m.FirstOrDefault<TypeValue>(It.IsAny<Expression`1>())
typeValueMockSet
.Setup(m => m.FirstOrDefault(It.IsAny<Expression<Func<TypeValue, bool>>>()))
.Returns(data.FirstOrDefault());
typeValueMockSet
.Setup(m => m.Add(It.IsAny<TypeValue>()))
.Returns((TypeValue vt) => vt)
.Callback((TypeValue vt) => data.Add(vt));
var mockContext = new Mock<EngineeringWorkEntities>();
mockContext.Setup(m => m.TypeValues).Returns(typeValueMockSet.Object);
int counter = 0;
mockContext
.Setup(m => m.SaveChanges())
.Returns(0)
.Callback(() => data
.ForEach(
(item) =>
{
item.Id = counter;
counter++;
}));
//Act
IMakro makroDateGenerate = new MakroDataGenerate(mockContext.Object);
var pObj = new PrivateObject(makroDateGenerate);
int r1 = (int)pObj.Invoke("GetTypeValueId", "0,2% k/k"); //2
You cannot mock FirstOrDefault because it is an extension method. It doesn't belongs to List<T> or anything else.
You cannot test private methods other than by using Reflection in the unit tests. This is very cumbersome. You should test the public interface anyway.
Either make the method public or internal. If you make it internal, you can allow the unit test project to see it by the use of the InternalsVisibleToAttribute.
[assembly:InternalsVisibleTo("MyUnitTestProject")]

Categories