# Nextcloud Impl Testing

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-impl-testing`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/Nextcloud-Claude-Skill-Package/tree/main/skills/source/nextcloud-impl/nextcloud-impl-testing

## Install

```sh
agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-impl-testing
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# nextcloud-impl-testing

## Quick Reference

### Test Types

| Type | Base Class | Bootstrap | Location |
|------|-----------|-----------|----------|
| Unit | `\Test\TestCase` | `../../tests/bootstrap.php` | `tests/Unit/` |
| Integration | `\Test\TestCase` | `../../tests/bootstrap.php` | `tests/Integration/` |
| Frontend | Jest + `@vue/test-utils` | `jest.config.js` | `tests/js/` or `src/__tests__/` |

### PHPUnit Configuration (`phpunit.xml`)

```xml

    
        
            ./tests/Unit
        
        
            ./tests/Integration
        
    
    
        
            ./lib
        
    

```

### Test Directory Structure

```
myapp/
├── phpunit.xml
├── tests/
│   ├── Unit/
│   │   ├── Controller/
│   │   │   └── NoteControllerTest.php
│   │   └── Service/
│   │       └── NoteServiceTest.php
│   └── Integration/
│       └── Service/
│           └── NoteServiceIntegrationTest.php
├── src/
│   └── __tests__/
│       └── NoteList.spec.ts
└── jest.config.js
```

### Mock Methods (PHPUnit)

| Method | Purpose |
|--------|---------|
| `$this->createMock(ClassName::class)` | Create mock with all methods stubbed |
| `$mock->method('name')->willReturn($val)` | Stub return value |
| `$mock->expects($this->once())->method('name')` | Assert method called once |
| `$mock->expects($this->never())->method('name')` | Assert method never called |
| `$mock->method('name')->willThrowException($e)` | Stub to throw exception |
| `$mock->method('name')->with($this->equalTo($v))` | Assert parameter value |

### Critical Warnings

**ALWAYS** call `parent::setUp()` in your `setUp()` method -- Nextcloud's TestCase performs essential initialization (database transaction start, cleanup hooks). Omitting it causes test pollution and random failures.

**ALWAYS** call `parent::tearDown()` in your `tearDown()` method -- Nextcloud's TestCase rolls back database transactions. Omitting it leaves test data in the database.

**ALWAYS** use `../../tests/bootstrap.php` as the PHPUnit bootstrap path -- this path is relative from your app's root to the Nextcloud server `tests/` directory. Without it, no Nextcloud classes are autoloaded.

**NEVER** use `\OCP\Server::get()` in tests to resolve services -- use constructor injection and mocks. Static container access makes tests non-isolated and order-dependent.

**NEVER** write to the real database in unit tests -- use mocks for `IDBConnection` and mappers. Reserve real database access for integration tests only.

**NEVER** skip the `phpunit.xml` configuration file -- without it, the bootstrap path is not set and test suites cannot be selected.

---

## Decision Tree: Which Test Type?

```
Need to test?
├── Pure logic (service, helper, utility)
│   └── UNIT TEST: Mock all dependencies
├── Controller request handling
│   └── UNIT TEST: Mock services, test return types/status codes
├── Database queries / mapper behavior
│   └── INTEGRATION TEST: Use real DI container + DB transactions
├── Multi-service interaction with real DI
│   └── INTEGRATION TEST: Resolve services from container
├── Vue component rendering
│   └── FRONTEND TEST: Jest + @vue/test-utils mount/shallowMount
└── API endpoint end-to-end
    └── INTEGRATION TEST: Use TestCase with real HTTP or container
```

---

## Essential Patterns

### Pattern 1: Unit Test with Mocks

```php
mapper = $this->createMock(NoteMapper::class);
        $this->service = new NoteService($this->mapper);
    }

    public function testFind(): void {
        $note = new Note();
        $note->setId(1);
        $note->setTitle('Test');

        $this->mapper->expects($this->once())
            ->method('find')
            ->with($this->equalTo(1), $this->equalTo('user1'))
            ->willReturn($note);

        $result = $this->service->find(1, 'user1');
        $this->assertEquals('Test', $result->getTitle());
    }

    public function testFindNotFound(): void {
        $this->mapper->expects($this->once())
            ->method('find')
            ->willThrowException(new DoesNotExistException(''));

        $this->expectException(NotFoundException::class);
        $this->service->find(999, 'user1');
    }
}
```

### Pattern 2: Controller Unit Test

```php
createMock(IRequest::class);
        $this->service = $this->createMock(NoteService::class);
        $this->controller = new NoteController(
            'myapp', $request, $this->service, 'user1'
        );
    }

    public function testShow(): void {
        $this->service->method('find')->willReturn(['id' => 1]);
        $result = $this->controller->show(1);
        $this->assertEquals(Http::STATUS_OK, $result->getStatus());
    }

    public function testShowNotFound(): void {
        $this->service->method('find')
            ->willThrowException(new NotFoundException());
        $result = $this->controller->show(999);
        $this->assertEquals(Http::STATUS_NOT_FOUND, $result->getStatus());
    }
}
```

### Pattern 3: Integration Test with DI Container

```php
getContainer();
        $this->service = $container->get(NoteService::class);
        $this->mapper = $container->get(NoteMapper::class);
    }

    public function testCreateAndFind(): void {
        $note = $this->service->create('Integration Test', 'content', 'testuser');
        $this->assertNotNull($note->getId());

        $found = $this->service->find($note->getId(), 'testuser');
        $this->assertEquals('Integration Test', $found->getTitle());
    }

    protected function tearDown(): void {
        parent::tearDown(); // CRITICAL: ALWAYS call parent (rolls back transaction)
    }
}
```

### Pattern 4: Frontend Test with Vue Test Utils

```typescript
// src/__tests__/NoteList.spec.ts
import { shallowMount } from '@vue/test-utils'
import NoteList from '../components/NoteList.vue'
import axios from '@nextcloud/axios'

jest.mock('@nextcloud/axios')
const mockedAxios = axios as jest.Mocked

describe('NoteList', () => {
    beforeEach(() => {
        jest.clearAllMocks()
    })

    it('renders notes after fetch', async () => {
        mockedAxios.get.mockResolvedValue({
            data: [{ id: 1, title: 'Note 1' }, { id: 2, title: 'Note 2' }],
        })

        const wrapper = shallowMount(NoteList)
        await wrapper.vm.$nextTick()
        await wrapper.vm.$nextTick() // Wait for async data

        expect(wrapper.findAll('.note-item')).toHaveLength(2)
    })

    it('shows empty state when no notes', async () => {
        mockedAxios.get.mockResolvedValue({ data: [] })

        const wrapper = shallowMount(NoteList)
        await wrapper.vm.$nextTick()
        await wrapper.vm.$nextTick()

        expect(wrapper.find('.empty-content').exists()).toBe(true)
    })
})
```

### Pattern 5: Jest Configuration

```javascript
// jest.config.js
module.exports = {
    testEnvironment: 'jsdom',
    moduleFileExtensions: ['js', 'ts', 'vue'],
    transform: {
        '^.+\\.ts$': 'ts-jest',
        '^.+\\.vue$': '@vue/vue3-jest',
        '^.+\\.js$': 'babel-jest',
    },
    moduleNameMapper: {
        '^@nextcloud/axios$': '/src/__mocks__/@nextcloud/axios.ts',
        '^@nextcloud/router$': '/src/__mocks__/@nextcloud/router.ts',
    },
    collectCoverageFrom: ['src/**/*.{ts,vue}', '!src/**/*.d.ts'],
}
```

---

## Running Tests

### PHP Tests

```bash
# Run all tests from app directory
cd apps/myapp
../../lib/composer/bin/phpunit

# Run specific test suite
../../lib/composer/bin/phpunit --testsuite unit
../../lib/composer/bin/phpunit --testsuite integration

# Run single test file
../../lib/composer/bin/phpunit tests/Unit/Service/NoteServiceTest.php

# Run single test method
../../lib/composer/bin/phpunit --filter testFind tests/Unit/Service/NoteServiceTest.php
```

### Frontend Tests

```bash
# Run from app directory
cd apps/myapp
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npx jest src/__tests__/NoteList.spec.ts
```

---

## Reference Links

- [references/methods.md](references/methods.md) -- TestCase base class, mock methods, assertion methods, phpunit.xml options
- [references/examples.md](references/examples.md) -- Complete unit test, integration test, and frontend test examples
- [references/anti-patterns.md](references/anti-patterns.md) -- Common testing mistakes and corrections

### Official Sources

- https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/testing.html
- https://docs.nextcloud.com/server/latest/developer_manual/basics/front-end/testing.html
- https://phpunit.de/documentation.html
- https://test-utils.vuejs.org/

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/Nextcloud-Claude-Skill-Package](https://github.com/Impertio-Studio/Nextcloud-Claude-Skill-Package)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-impl-testing
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
