Heey all,
I'm having troubles to set up a testcase.
I have a plain symfony 3 project connected to mongodb. I have multiple documents which each needs an extra method to query the database. The method will get the last document inserted in the collection and is called findLatestInserted().
This specific function was duplicated in each document repository. So I decided to extract it and create a class BaseDocumentRepository which extends the default DocumentRepository. All of my document repositories still have their own DocumentRepository class let's say: CpuInfoRepository, RamInfoRepository. These classes do offer a few extra methods to query the mongodb database and the one in common: findLatestInserted()
It all works fine but just in case i'd wanted to write a unit test for this method findLatestInserted().
I have a test database called prototyping-test which is used to create a document and query it and check the result. Afterwards it'll clear itself so no documents stays in. For each repository there's a specific url to post data to to create a file in the database. To create a CpuInfo collection you'll post data to http://localhost:8000/ServerInfo/CreateCpuInfo. To create a RamInfo collection you'll post data to http://localhost:8000/ServerInfo/CreateRamInfo.
So here follows my question how would i write a test to test the method findLatestInserted()?
this is what i've tried so far:
public function testFindLatestInserted()
{
$client = self::createClient();
$crawler = $client->request('POST',
'/ServerInfo/CreateCpuInfo',
[
"hostname" => $this->hostname,
"timestamp" => $this->timestamp,
"cpuCores" => $this->cpuCores,
"cpu1" => $this->cpu1,
"cpu2" => $this->cpu2
]);
$this->assertTrue($client->getResponse()->isSuccessful());
$serializer = $this->container->get('jms_serializer');
$cpuInfo = $serializer->deserialize($client->getResponse()->getContent(), 'AppBundle\Document\CpuInfo', 'json');
$expected = $this->dm->getRepository("AppBundle:CpuInfo")->find($cpuInfo->getId());
$stub = $this->getMockForAbstractClass('BaseDocumentRepository');
$actual = $this->dm
->getRepository('AppBundle:CpuInfo')
->findLatestInserted();
$this->assertNotNull($actual);
$this->assertEquals($expected, $actual);
}
At the line $actual = $this->dm->getRepository('AppBundle:CpuInfo')->findLatestInserted(); i got stuck. As this would only test for CpuInfo while there is RamInfo too (and some other classes not mentioned here). How would one approach this setting?
I specificly want to test the method findLatestInserted() on the level of the abstract class instead of the concrete classes.
Please help me out!
Instead of testing the whole stack, just concentrate on testing findLatestInserted() in concrete classes.
Inject MondoDB stub into AppBundle:CpuInfo and check if findLatestInserted() returns expected value.
Do the same for AppBundle:RamInfo.
Avoid testing abstract classes, always test concrete classes.
In future, you may decide not to inherit from BaseDocumentRepository and may not notice that new implementation of findLatestInserted() fails.
Related
I am desperately waiting for someone attention to get my question answered.... please help..
ModelAdmin model has to export to Excel action method.
I need to access related model fields in action method. That means I can not pass any arguments therefore I tried relatedmodel_set but ModelAdmin action method shows memory location and fails when I try to access values through attributes:
<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager..RelatedManager object at 0x7f8eea904ac0>
model.py
class EnrolStudent(models.Model):
def get_trn_activity(self):
return self.studenttraininactivities_set
class StudentTraininActivities(models.Model):
trainin_activities = models.ForeignKey(EnrolStudent,
on_delete=CASCADE, null=True )
<other fields...>
admin.py
#admin.register(EnrolStudent)
class EnrolAdmin(admin.ModelAdmin):
form = CityInlineForm
inlines = [CohortTraininActivitiesInline]
...
actions = [export_as_txt_action_0120("File NAT00120 data Export"
, fields=['information_no', 'get_trn_activity',
'student_enrol__student_code'])]
I need to access related model fields to export to excel.
I can not pass parameter to get_trn_activity as you have noticed.
Therefore selected rows only data from Django admin change_list page will only need bit of work using its queryset in actions method used in separate actions.py file that I can do!
Please help me with this issue. I am new to Python / Django.
I also tried property decorator in related model then access in a method in main model then call it inside action but same problem with memory address not the direct value and then how to get data of memory location here .... I don't know.
If I can access the related fields then I can do it no issue.
Another question:
I had same situation with model/related model before, but they were connected through OneToOneField relationship and I was able to use dundor to access related model fields but in this case of ForiegnKey relationship I can not see related model in queryset.
In other case this is what I can do easily; here cohortdetails is related model and when I debug I saw it was listed in queryset that was great.
actions = [export_as_txt_action_0080("File NAT00080 txt Export",
fields=['rto_student_code', 'first_name', 'family_name'
,'cohortdetails__highest_school__highestschool_levelcode',
'cohortdetails__cohort_gender'
, 'cohortdetails__student_dob' ])]
My question is this: How can you implement a default server-side "filter" for a navigation property?
In our application we seldom actually delete anything from the database. Instead, we implement "soft deletes" where each table has a Deleted bit column. If this column is true the record has been "deleted". If it is false, it has not.
This allows us to easily "undelete" records accidentally deleted by the client.
Our current ASP.NET Web API returns only "undeleted" records by default, unless a deleted argument is sent as true from the client. The idea is that the consumer of the service doesn't have to worry about specifying that they only want undeleted items.
Implementing this same functionality in Breeze is quite simple, at least for base entities. For example, here would be the implementation of the classic Todo's example, adding a "Deleted" bit field:
// Note: Will show only undeleted items by default unless you explicitly pass deleted = true.
[HttpGet]
public IQueryable<BreezeSampleTodoItem> Todos(bool deleted = false) {
return _contextProvider.Context.Todos.Where(td => td.Deleted == deleted);
}
On the client, all we need to do is...
var query = breeze.EntityQuery.from("Todos");
...to get all undeleted Todos, or...
var query = breeze.EntityQuery.from("Todos").withParameters({deleted: true})
...to get all deleted Todos.
But let's say that a BreezeSampleTodoItem has a child collection for the tools that are needed to complete that Todo. We'll call this "Tools". Tools also implements soft deletes. When we perform a query that uses expand to get a Todo with its Tools, it will return all Tools - "deleted" or not.
But how can I filter out these records by default when Todo.Tools is expanded?
It has occurred to me to have separate Web API methods for each item that may need expanded, for example:
[HttpGet]
public IQueryable<Todo> TodoAndTools(bool deletedTodos = false, bool deletedTools = false)
{
return // ...Code to get filtered Todos with filtered Tools
}
I found some example code of how to do this in another SO post, but it requires hand-coding each property of Todo. The code from the above-mentioned post also returns a List, not an IQueryable. Furthermore this requires methods to be added for every possible expansion which isn't cool.
Essentially what I'm looking for is some way to define a piece of code that gets called whenever Todos is queried, and another for whenever Tools is queried - preferably being able to pass an argument that defines if it should return Deleted items. This could be anywhere on the server-side stack - be it in the Web API method, itself, or maybe part of Entity Framework (note that filtering Include extensions is not supported in EF.)
Breeze cannot do exactly what you are asking for right now, although we have discussed the idea of allowing the filtering of "expands", but we really need more feedback as to whether the community would find this useful. Please add this to the breeze User Voice and vote for it. We take these suggestions very seriously.
Moreover, as you point out, EF does not support this.
But... what you can do is use a projection instead of an expand to do something very similar:
public IQueryable<Object> TodoAndTools(bool deleted = false
,bool deletedTools = false) {
var baseQuery = _contextProvider.Context.Todos.Where(td => td.Deleted == deleted);
return baseQuery.Select(t => new {
Todo: t,
Tools: t.Tools.Where( tool => tool.Deleted = deletedTools);
});
}
Several things to note here:
1) We are returning an IQueryable of Object instead of IQueryable of ToDo
2) Breeze will inspect the returned payload and automatically create breeze entities for any 'entityTypes' returned (even within a projection). So the result of this query will be an array of javascript objects each with two properties; 'ToDo' and 'Tools' where Tools is an array of 'Tool' entities. The nice thing is that both ToDo and Tool entities returned within the projection will be 'full' breeze entities.
3) You can still pass client side filters based on the projected property names. i.e.
var query = EntityQuery.from("TodoAndTools")
.where("Todo.Description", "startsWith", "A")
.using(em);
4) EF does support this.
I'm doing the following using Mongoose:
that.model.update({_id: dao._id}, dao, { upsert: true }, cb);
Where dao is a mongoose representation containing (among other things) a couple of embedded documents. As a test I've deleted a couple of the embedded docs from the array before calling the update-method above.
The result is that the change to the array of embedded docs IS NOT persisted.
Anything I'm overlooking?
Hard to be certain w/o seeing more code, but if dao is a Mongoose model instance, you should be calling dao.save(cb); instead.
I solved the problem by doing something similar as proposed in the following issue: https://github.com/LearnBoost/mongoose/issues/571
For completeness some background which led to the problem.
I'm using DDD repositories which are populated on app-start. Under the hood this fetches Mongoose-objects (which are treate as DAOs in my situation) and are translated to domainobjects, which are cached in the repository. I need this separation between domainobjects and mongoose-objects, don't ask.
This means that getById, getAll and all other public interfaces of the repo work with domainobjects and not with mongoose-objects.
When doing things like add or update on the repo this internally only updates the in-mem cache (which, again, only uses domainobjects instead of mongoose-objects)
Only when doing commit on the repo does the possibly changed collection of domainobjects get persisted. This is done by creating NEW Mongoose-objects instead of fetching Existing mongoose-objects and updating those.
This is why I can't use dao.save() since, when I'm saving a different (just created) mongoose-object while a mongoose-object with the same id may possibly already exist in Mongo, it throws a duplicate id error.
Some relevant snippet from by code illustrating the solution:
var dao = that.createDAO(domainobject);
//https://github.com/LearnBoost/mongoose/issues/571
// Convert the Model instance to a simple object using Model's 'toObject' function
// to prevent weirdness like infinite looping...
var upsertData = dao.toObject();
// Delete the _id property, otherwise Mongo will return a "Mod on _id not allowed" error
delete upsertData._id;
that.model.update({_id: dao._id}, upsertData, { upsert: true }, cb);
I have read so much (dozens of posts) about one thing:
How to unit test business logic code that has Entity Framework code in it.
I have a WCF service with 3 layers :
Service Layer
Business Logic Layer
Data Access Layer
My business logic uses the DbContext for all the database operations.
All my entities are now POCOs (used to be ObjectContext, but I changed that).
I have read Ladislav Mrnka's answer here and here on the reasons why we should not mock \ fake the DbContext.
He said:
"That is the reason why I believe that code dealing with context / Linq-to-entities should be covered with integration tests and work against the real database."
and:
"Sure, your approach works in some cases but unit testing strategy must work in all cases - to make it work you must move EF and IQueryable completely from your tested method."
My question is - how do you achieve this ???
public class TaskManager
{
public void UpdateTaskStatus(
Guid loggedInUserId,
Guid clientId,
Guid taskId,
Guid chosenOptionId,
Boolean isTaskCompleted,
String notes,
Byte[] rowVersion
)
{
using (TransactionScope ts = new TransactionScope())
{
using (CloseDBEntities entities = new CloseDBEntities())
{
User currentUser = entities.Users.SingleOrDefault(us => us.Id == loggedInUserId);
if (currentUser == null)
throw new Exception("Logged user does not exist in the system.");
// Locate the task that is attached to this client
ClientTaskStatus taskStatus = entities.ClientTaskStatuses.SingleOrDefault(p => p.TaskId == taskId && p.Visit.ClientId == clientId);
if (taskStatus == null)
throw new Exception("Could not find this task for the client in the database.");
if (taskStatus.Visit.CustomerRepId.HasValue == false)
throw new Exception("No customer rep is assigned to the client yet.");
TaskOption option = entities.TaskOptions.SingleOrDefault(op => op.Id == optionId);
if (option == null)
throw new Exception("The chosen option was not found in the database.");
if (taskStatus.RowVersion != rowVersion)
throw new Exception("The task was updated by someone else. Please refresh the information and try again.");
taskStatus.ChosenOptionId = optionId;
taskStatus.IsCompleted = isTaskCompleted;
taskStatus.Notes = notes;
// Save changes to database
entities.SaveChanges();
}
// Complete the transaction scope
ts.Complete();
}
}
}
In the code attached there is a demonstration of a function from my business logic.
The function has several 'trips' to the database.
I don't understand how exactly I can strip the EF code from this function out to a separate assembly, so that I am able to unit test this function (by injecting some fake data instead of the EF data), and integrate test the assembly that contains the 'EF functions'.
Can Ladislav or anyone else help out?
[Edit]
Here is another example of code from my business logic, I don't understand how I can 'move the EF and IQueryable code' out from my tested method :
public List<UserDto> GetUsersByFilters(
String ssn,
List<Guid> orderIds,
List<MaritalStatusEnum> maritalStatuses,
String name,
int age
)
{
using (MyProjEntities entities = new MyProjEntities())
{
IQueryable<User> users = entities.Users;
// Filter By SSN (check if the user's ssn matches)
if (String.IsNullOrEmusy(ssn) == false)
users = users.Where(us => us.SSN == ssn);
// Filter By Orders (check fi the user has all the orders in the list)
if (orderIds != null)
users = users.Where(us => UserContainsAllOrders(us, orderIds));
// Filter By Marital Status (check if the user has a marital status that is in the filter list)
if (maritalStatuses != null)
users = users.Where(pt => maritalStatuses.Contains((MaritalStatusEnum)us.MaritalStatus));
// Filter By Name (check if the user's name matches)
if (String.IsNullOrEmusy(name) == false)
users = users.Where(us => us.name == name);
// Filter By Age (check if the user's age matches)
if (age > 0)
users = users.Where(us => us.Age == age);
return users.ToList();
}
}
private Boolean UserContainsAllOrders(User user, List<Guid> orderIds)
{
return orderIds.All(orderId => user.Orders.Any(order => order.Id == orderId));
}
If you want to unit test your TaskManager class, you should employ the Repository dessign pattern and inject repositories such as UserRepository or ClientTaskStatusRepository into this class. Then instead of constructing CloseDBEntities object you will use these repositories and call their methods, for example:
User currentUser = userRepository.GetUser(loggedInUserId);
ClientTaskStatus taskStatus =
clientTaskStatusRepository.GetTaskStatus(taskId, clientId);
If yout wanto to integration test your TaskManager class, the solution is much more simple. You just need to initialize CloseDBEntities object with a connection string pointing to the test database and that's it. One way how to achieve this is injecting the CloseDBEntities object into the TaskManager class.
You will also need to re-create the test database before each integration test run and populate it with some test data. This can be achieved using Database Initializer.
There are several misunderstandings here.
First: The Repository Pattern. It's not just a facade over DbSet for unit testing! The repository is a pattenr strongly related to Aggregate and Aggreate Root concepts of Domain Driven Design. An aggregate is a set of related entities that should stay consistent to each other. I mean a business consistency, not just only a foreign keys validity. For example: a customer who have made 2 orders should get a 5% discount. So we should somehow manage the consistency between the number of order entities related to a customer entity and a discount property of the customer entity. A node responsible for this is an aggregate root. It is also the only node that should be accessible directly from outside of the aggregate. And the repository is an utility to obtain an aggregate root from some (maybe persistent) storage.
A typical use case is to create a UoW/Transaction/DbContext/WhateverYouNameIt, obtain one aggregate root entity from the repository, call some methods on it or access some other entities by traversing from the root, Commit/SaveChanges/Whatever. Look, how far it differs from yur samples.
Second: The Business Logic. I've already showed you one example: a customer who have made 2 orders should get a 5% discount. In contrary: your second code sample is not a business logic. It's just a query. The responsibility of this code is to obtain some data from the storage. In such a case, the storage technology behind it does matter. So I would recomend integration tests here rather than pretending the storage doesn't matter when interacting with the storage is the sole purpose of this function.
I would also encapsulate that in a Query Object that was already suggested. Then - such a query object could be mocked. Not just DbContext behind it. The whole QO.
The first code sample is a bit better because it probably ivolves some business logic, but that's dificult to identify. Wich leads us to the third problem.
Third: Anemic Domain Model. Your domain doesnt' look very object oriented. You have some dumb entities and transaction scripts over them. With 7 parameters! Thats pure procedural programming.
Moreover, in your UpdateTaskStatus use case - what is the aggregate root? Befere you answer that, the most important question first: what exactly do you want to do? Is that... hmm... marking a current task of a user done when he was visited? Than, maybe there should be a method Visit() inside a Customer Entity? And this method should have something like this.CurrentTaskStatus.IsCompleted = true?
That was just a random guess. If I missed, that would clearly show another issue. The domain model should use the ubiquitous language - something common for the programmer and a business. Your code doesn't have that expressive power that a common language gives. I just don't know what is going on there in UpdateTaskStatus with 7 parameters.
If you place proper expressive methods for performing business operations in your entities that will also enforce you to not use DbContext there at all, as you need your entities to stay persistence ignorant. Then the problem with mocking disappears. You can test the pure business logic without persistence concerns.
So the final word: Reconsider your model first. Make your API expressive by using ubiquitous language first.
PS: Please don't treat me as an authority. I may be completely wrong as I'm just starting to learn DDD.
using the unit of work and repository patterns i recently came across the issue, that changes to the unit of work are not reflected to subsequent queries. Example:
var ctx = DIContainer.Current.Resolve<IB2bContext>();
var rep = DIContainer.Current.Resolve<IRepository<Word>>(
new DependencyOverride<IB2bContext>(ctx));
rep.Add(new Word () { "One" };
rep.Add(new Word () { "Two" };
rep.GetAll().ToList().ForEach(i =>
Console.Write(i.text)); // nothing seen here
So in other words, unless i call SaveChanges() to persist the objects into the Database, i dont see them. Well ofcause i can fiddle around with the ChangeTracker and/or do things like context.Entry(foo).Property(...).CurrentValue. But does that play with a ddd like decoupling of layers? I dont think so. And where is my consistent dataview that once was called a database transaction?
Please enlighten me.
Armin
Your repository exposes some GetAll method. The method itself executes database query. If you want to see local data not inserted to database you must add them to result set. For example like:
public IEnumerable<Word> GetAll()
{
DbSet<Word> set = context.Set<Word>();
return set.AsEnumerable().Concat(set.Local);
}
The query execution is only responsible for returning persisted (real) data.