This question is one in a series I seem to be generating as I slowly pick my way through learning testing.
I have a book model and a ticketaudit model. They have a relationship one to many. When a book is created a function should also create a range of tickets (for audit).
I want my test to make sure the ticketAudit model is being created and the association being made using the eloquent ORM within laravel.
my class so far:
Class TicketCreator implements TicketCreatorInterface {
protected $ticket;
public function __construct(TicketAudit $ticketAudit)
{
//dd($ticketAudit);
$this->ticket = $ticketAudit;
}
public function createTicket($input, $book) {
$counter = $input['start'];
while($counter <= $input['end']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
}
and my attempts at a test:
public function testCreateCreatesTickets() {
//arrange
$book = FactoryMuff::create('Book');
$aTicket = FactoryMuff::create('TicketAudit');
$ticketAudit = new TicketAudit;
$ticketCreator = new TicketCreator($ticketAudit);
//act
$response = $ticketCreator->createTicket(array('start'=>1000, 'end'=>1001), $book);
// Assert...
$this->assertEquals(true, $response);
}
However when I run this test I get the error:
Integrity constraint violation: 19 ticket_audits.ticketnumber may not be NULL
For some reason the model is not being created with the values I pass to it. I've checked in the function that the object exists and also the values are being created correctly in the array but it doesnt work.
Is this unique to testing?
I am creating an sqlite in memory database for this test.
Any help appreciated
Crikey this decision to start testing is a bit of a nightmare
Thanks to Manuel's request to post the TicketAudit class I noticed my model extended Eloquent. I had recently added Ardent and should have extended Ardent so the error lay in the model!
Revised corrected model :
use LaravelBook\Ardent\Ardent;
class TicketAudit extends Ardent {
protected $guarded = array();
public $autoHydrateEntityFromInput = true;
public $autoPurgeRedundantAttributes = true;
public static $rules = array(
'status' => 'required',
'ticketnumber' => 'required'
);
public static $factory = array(
'ticketnumber' => '1000',
'status' => 'unused',
);
public function book() {
return $this->belongsTo('Book');
}
}
Thank you for the sign post
Related
I'm using xUnit & Moq and what I'm trying to achieve is typically create Identity User so it's stored in Entity Framework In-Memory DB.
I have established Seed Data functionality - it gets triggered when my ASP.NET Core host app starts and works flawlessly - so no problem.
But the issue occurs when I use a mocked UserManager. No exceptions are thrown, users are just not being saved.
Verified while debugging tests, DbContext returns 0 users, also UserManager.FindByNameAsync yields null.
I wonder what is the cause. Could it be due to the way I assemble UserManager in constructor of SeedDataTest class?
public class SeedDataTest
{
private AppDbContext dbContext;
private UserManager<ApplicationUser> userManager;
private ITenantService tenantService;
public SeedDataTest()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "in_memory_db")
.Options;
dbContext = new AppDbContext(options);
var userStore = new UserStore<ApplicationUser>(dbContext);
userManager = new Mock<UserManager<ApplicationUser>>(
userStore,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<ApplicationUser>>().Object,
new IUserValidator<ApplicationUser>[0],
new IPasswordValidator<ApplicationUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
.Object;
tenantService = new Mock<TenantService>(dbContext).Object;
}
[Fact]
public void Test1()
{
new TenantsCreator(dbContext).Create();
new UserCreator(dbContext, tenantService, userManager).Create(); // stuck here
new MembershipCreator(dbContext, userManager).Create();
// unfinished
}
}
And here is the code from UserCreator
public class UserCreator
{
private AppDbContext _context;
private ITenantService _tenantService;
private UserManager<ApplicationUser> _userManager;
public UserCreator(
AppDbContext context,
ITenantService tenantService,
UserManager<ApplicationUser> userManager
)
{
_context = context;
_tenantService = tenantService;
_userManager = userManager;
}
public void Create()
{
Task.Run(async () => await CreateUsers()).ConfigureAwait(false).GetAwaiter().GetResult();
}
private async Task CreateUsers()
{
ApplicationUser hostAdminUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.AdminJoe.UserName));
if (hostAdminUser == null)
{
hostAdminUser = new ApplicationUser()
{
FirstName = SetupConsts.Users.AdminJoe.FirstName,
LastName = SetupConsts.Users.AdminJoe.LastName,
UserName = SetupConsts.Users.AdminJoe.UserName,
Email = SetupConsts.Users.AdminJoe.Email,
EmailConfirmed = true,
PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(hostAdminUser, SetupConsts.Users.Passwords.Default)
};
await _userManager.CreateAsync(hostAdminUser);
}
ApplicationUser secondaryUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.JohnRoe.UserName));
if (secondaryUser == null)
{
secondaryUser = new ApplicationUser()
{
FirstName = SetupConsts.Users.JohnRoe.FirstName,
LastName = SetupConsts.Users.JohnRoe.LastName,
UserName = SetupConsts.Users.JohnRoe.UserName,
Email = SetupConsts.Users.JohnRoe.Email,
EmailConfirmed = true,
PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(secondaryUser, SetupConsts.Users.Passwords.Default)
};
await _userManager.CreateAsync(secondaryUser);
}
}
}
we use mocking frameworks to allow us to build mocked (substituted) dependencies as well as specify what is returned from classes/interfces used within our SUT.
A good use case for this is a database itself. We always want to know the exact state of objects whilst we are testing them and the only way we do that is by expressing the content of them ourselves.
That is where Moq comes in. One of its features is to allow us to state the result of method calls.
I believe you are seeing 0 results returned because you are using a moq implementation of a class (which doesnt actually call the implemented class). In order to get a result, you will need to do some setup:
mockedClass.Setup(x => x.GetUserDetails(It.IsAny<int>())).Returns(new UserDetails());
Either do it this way, or ensure you are passing a concrete implementation of the UserManager class rather than the mocked version:
userManager = new UserManager<ApplicationUser>
Hope that helps
I am trying to test a Service which preprocessed a form and finally saves it. Within the creating of that form:
$this->container->get('security.token_storage')->getToken()->getUser();
is called to get currently logged in user as a default value for a field.
Right now I am having this (extending Symfony\Bundle\FrameworkBundle\Test\WebTestCase):
private $pages;
private $formFactory;
protected function setUp()
{
self::bootKernel();
$client = self::$kernel->getContainer()->get('test.client');
$client->setServerParameters([
'HTTP_HOST' => 'ajax.localhost.dev:10190',
'CONTENT_TYPE' => 'application/json',
'HTTP_X-Requested-With' => 'XMLHttpRequest',
'HTTP_USER_AGENT' => 'Symfony/2.0',
'PHP_AUTH_USER' => 'root',
'PHP_AUTH_PW' => 'root#localhost.dev'
]);
$this->pages = self::$kernel->getContainer()->get('app.pages');
$this->formFactory = self::$kernel->getContainer()->get('form.factory');
}
public function testNewPage() {
$page = new Page();
//shortened
$form = $this->formFactory->create(PageType::class, $page);
}
But that gives me the error:
Call to a member function getUser() on null
What shows that there is no security token.
How can I come over that?
UPDATE
Thanks to the comments of #LBA I tried that code, with no luck:
$session = self::$kernel->getContainer()->get('session');
$token = new UsernamePasswordToken('root', 'root', 'main', ['ROLE_USER', 'ROLE_ROOT']);
$session->set('_security_main', serialize($token));
$session->save();
The part with setting a Cookie as described here is missing, since the $kernel has no method getCookieJar()
I could finally make it work like so:
protected function setUp()
{
self::bootKernel();
$root = new User();
$root->setUsername('root');
$root->setPassword('root');
$root->setEmail('root#localhost.dev');
$token = new UsernamePasswordToken($root, null, 'main', ['ROLE_USER', 'ROLE_ROOT']);
self::$kernel->getContainer()
->get('security.token_storage')
->setToken($token);
$this->pages = self::$kernel->getContainer()->get('app.pages');
$this->formFactory = self::$kernel->getContainer()->get('form.factory');
}
BUT BUT BUT Even if it is possible to solve that problem, the real issue in this case is, to have that $this->container->get('security.token_storage')->getToken()->getUser(); call with the Form, since this breaks the form in a test case. The pattern to prevent such a thing from happening is dependency injection, what I have missed to apply on the form type.
So the better solution would be (in the Form extending AbstractType):
public function configureOptions(OptionsResolver $resolver)
{
$this->setDefined(['user]);
}
And finally create the form like so (in Controller or TestCase)
Within a UnitTest:
$user = new User();
and in the controller:
$user = $this->container->get('security.token_storage')->getToken()->getUser();
$form = $this->formFactory->create(TheFormType::class,
<some data object>,
['user' => $user]);
I am trying to test a repository and so need to mock the Model Container. Really all I need is to be able to set the Blogs returned to GetBlogs() in the BlogRepository. The repository code is:
private BlogWebsiteModelContainer context;
public BlogRepository(BlogWebsiteModelContainer context)
{
this.context = context;
}
public IEnumerable<Blog> GetBlogs()
{
return context.Blogs;
}
So I want to be able to set what context.Blogs is. I am using Moq and have tried the following:
var mockBlogSet = new Mock<DbSet<Blog>>();
context.Setup(m => m.Blogs).Returns(mockBlogSet.Object);
blogRepo = new BlogRepository(context.Object);
But I get this error message when I debug:
Invalid setup on a non-virtual (overridable in VB) member: m => m.Blogs
Any help greatly appreciated.
Create an interface for your BlogWebsiteModelContainer, then mock the interface. Also instead of defining Blogs on the interface as DbSet<Blog> define it as IQuerayable<Blog>
You can then create a List and use the .AsQueryable extension:
var contextMock = new Mock<IBlogWebsetModelContainer>();
var mockBlogSet = new List<Blog>();
contextMock.Setup(m => m.Blogs).Returns(mockBlogSet.AsQueryable());
blogRepo = new BlogRepository(contextMock.Object);
I found the answer at this link http://msdn.microsoft.com/en-us/data/dn314429.aspx. And so my code became the following:
List<Blog> blogs = new List<Blog>
{
new Blog() { Id = 1 },
new Blog() { Id = 2 },
new Blog() { Id = 3 },
};
var data = blogs.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BlogWebsiteModelContainer>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
blogRepo = new BlogRepository(mockContext.Object);
Then I had to go into my BlogWebsiteModelContainer class and change:
public DbSet<Blog> Blogs { get; set; }
to
public virtual DbSet<Blog> Blogs { get; set; }
Just adding the virtual in. And it all worked.
Quick note: reason for creating the list first and then making it .AsQueryable() was so in my code I had the original blog list separate so I could compare it in my tests.
I'm just starting out with TDD and I'm trying to test validation in a model. Currently, all my tests pass, but I believe that they shouldn't. The testBodyIsRequiredToValidate() should fail since I have not added it to the rules array. When I remove the title required from the rules array, all my tests fail as expected. However, adding title required causes them all to pass.
My Tests:
class PostTest extends TestCase {
public function testTitleIsRequiredToValidate()
{
$post = new Post;
$post->body = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
public function testBodyIsRequiredToValidate()
{
$post = new Post;
$post->title = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
}
My Model:
class Post extends Eloquent {
public function validate($input)
{
$rules = array(
'title' => 'required'
);
$v = Validator::make($input, $rules);
if ($v->passes()) {
return true;
} else {
return false;
}
}
}
This happens because your validation is wrong. You pass the model itself to the validator but you should pass an associative array.
When you delete title from the rules you pass an empty rule, so the validation will be always true, thats why all your test fail.
When you add title to your rules, the validations will be always false hence all your tests pass, because the validator's first parameter doesn't have an array element called title.
One way to correct this is to change your validation line to this:
$v = Validator::make(array('title' => $input['post']->title), $rules);
Here you get the $post from your $input array, because compact() will put it to an array with the key of the variable name. Then you access the title property of the object and make this to the value of the title key so the validator will be able to find and validate it.
I have to mention that your validation technique is kinda bad for me. You pass the object in an array to a function of the same object. Why don't you get the object in the function?
I would write the validator function like this:
public function validate() {
$rules = array(
'title' => 'required'
);
$v = Validator::make($this->toArray(), $rules);
return $v->passes();
}
So I could call the function like this in the tests:
$this->assertFalse($post->validate());
i am writing webservice in symfony2 but i facing some problem regarding the output ,as it is giving blank output.
class DefaultController extends Controller {
/**
*
* #Route("/webservices/activity/{id}", name="user_json_activity")
* #Method("get")
*/
public function activityAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$list = $em->getRepository('FitugowebserviceBundle:activity')->findOneById($id);
$r_array = $this->routes2Array($list);
$r = array('activity' => $r_array);
return new Response(json_encode($r));
}
private function routes2Array($routes) {
$points_array = array();
foreach ($routes as $route) {
$r_array = array('activity' => $route->getActivity(),
'icon' => $route->getIcon());
$points_array[] = $r_array;
}
return $points_array;
}
}
When i try to fetch data for id=1 http://domain.org/fitugo/web/app_dev.php/webservices/activity/1 it is giving output as follows
{"activity":[]}
It look very strange that you want get array with findOneById method. The first thing I suggest to add a check that the entity founded by id exist. Then look that findOneById returns and check your controller logic.